==== Linux environment ==== How to set up a headless build environment. |OS|Ubuntu 20.04| |Android SDK|Android 11 (API level 30) as of 26.11.2021| ==== Install android-sdk on Ubuntu ==== # install openjdk8 - tested android sdk and its compatible sudo apt-get install openjdk-8-jdk # activate the 8th version if you have multiple using: # sudo update-alternatives --config java # install android-sdk under /usr/lib/android-sdk/ sudo apt update && sudo apt install android-sdk # download the commandlinetools and add them to sdk # /cmdline-tools/latest/ wget -P /tmp/ https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip # check https://stackoverflow.com/questions/60440509/android-command-line-tools-sdkmanager-always-shows-warning-could-not-create-se sudo mkdir -p /usr/lib/android-sdk/cmdline-tools/ sudo unzip /tmp/commandlinetools-*.zip -d /usr/lib/android-sdk/cmdline-tools/ sudo mv /usr/lib/android-sdk/cmdline-tools/cmdline-tools/ /usr/lib/android-sdk/cmdline-tools/tools/ # open bashrc and modify the environment variables vim ~/.bashrc export ANDROID_HOME="/usr/lib/android-sdk/" export PATH=$PATH:$ANDROID_HOME/cmdline-tools/tools/bin:$ANDROID_HOME/platform-tools # apply changes in bashrc source ~/.bashrc # setting the rights sudo chown $USER:$USER $ANDROID_HOME -R # can successfully access the sdkmanager sdkmanager --version # install the platform tools https://developer.android.com/studio/releases/platform-tools sdkmanager "platform-tools" "platforms;android-29" # install the build tools https://developer.android.com/studio/releases/build-tools sdkmanager "build-tools;29.0.3" # confirm all the licenses for the android tools otherwise they wont work yes | sdkmanager --licenses # check for android sdk updates sdkmanager --update # you can build android project now cd ./gradlew build ==== HOWTO ==== === Plan app's Layout === * use Fragments for top level elements which may be reused, since fragments **can not be nested**. * implement Views with custom Layout, implemented in **xml** to use these blocks on deeper levels. \\ These views can be nested. \\ implement layout in xml and assign the xml layout to the view (see Create a View class which will wrap the XML Layout) ==== Declaring custom XML attributes ==== An in detail describtion about declaring custom XML attributes for custom views can be found here: [[http://stackoverflow.com/questions/2695646/declaring-a-custom-android-ui-element-using-xml]] ACHTUNG: - Nutze diesen Namespace, statt den der eigenen App: xmlns:app="http://schemas.android.com/apk/res-auto" ==== Fading Background ==== Is described here: http://nathanael.hevenet.com/android-dev-fading-background-animation/ ==== ListView ==== == Select Item == listView.setItemChecked(1, true); == Iterate Items== ContentListAdapter contentListAdapter = (ContentListAdapter) listView.getAdapter(); for(int i=0; i ==== Using JAR Libs ==== A Jar can be added to android, by putting it into the **libs** folder! **ACHTUNG:** Adroid kann nur JARs nutzen, die mit dem Compiler 1.7 gebaut wurden. Falls dies nicht der Fall ist - wird eine NoClassDefFound exception geworfen. {{http://i520.photobucket.com/albums/w327/schajtan/2013-08-20_11-09-46_zps4723ec3c.png}} ==== Tools ==== The tool monitor.bat can be used as a Standalone logger! Details are [[http://developer.android.com/tools/debugging/ddms.html#running|here]] {{http://i520.photobucket.com/albums/w327/schajtan/2013-09-02_11-00-07_zps3bb644a0.png}} === Aapt === Inspect the APK is done via D:\Development\Eclipse Juno - Android\sdk\build-tools\android-4.2.2\aapt.exe dump badging theApp.apk === Unpackaging the APK is done via === https://code.google.com/p/android-apktool/downloads/detail?name=apktool-install-windows-2.2_r01-3.tar.bz2 apktool d HelloWorld.apk ./HelloWorld === Genimotion === Genimotion is a nice and fast Android VM. It may support ARM Architecture, but this option must be enabled. How to do that is described here: http://forum.xda-developers.com/showthread.php?t=2528952 ==== Maven ==== == Dependncies == **ACHTUNG:** For some reason the dependencies form parent pom are not inherited to the child pom. So always add dependencies directly to children Dependency to my own apklib: The apklib barcode-lib-commons-dataproject BarcodeLibCommonsDataProject 1.0.0 apklib The dependency de.ivu.fare.apps-13.2.PIROL barcode-lib-commons-dataproject 1.0.0 apklib ==Package type== The apk is an App. barcodelib2 1.0.0 apk Barcode-Lib2 The apklib - is the android lib project barcodelib2 1.0.0 apklib Barcode-Lib2 ==Signieren== Wie man Maven einrichtet, um eine signierte APK bauen zu können steth hier: \\ http://www.simpligility.com/2010/07/sign-zipalign-and-to-market-to-market-with-maven/ Danach kann eine signierte **apk** z.B. durch erzeugt werden: mvn clean install ==ZipAlign== TO do the zipalign - use the code as described here: http://stackoverflow.com/questions/10915922/how-to-check-that-maven-goal-called-during-phase DO not forget the **false** com.jayway.maven.plugins.android.generation2 android-maven-plugin 3.2.0 true 8 2.3.3_API-10 true ${project.build.directory}/filtered-assets ${project.build.directory}/filtered-manifest/AndroidManifest.xml false ${build.verbosity} ${project.build.directory}/${project.artifactId}-${build.version.name}.apk ${project.build.directory}/${project.artifactId}-${build.version.name}-aligned.apk zipalign package zipalign ==Release== Bauene in release mode, so dass die Variable **BuildConfig.DEBUG==false** ist: ist beim plugin **android-maven-plugin** am besten über einen Parameter **-Dandroid.release=true** steuerbar. Die Details sind hier beschrieben: [[http://stackoverflow.com/questions/15055961/android-maven-plugin-disable-debug-build-for-apk|http://stackoverflow.com/questions/15055961/android-maven-plugin-disable-debug-build-for-apk]] clean install -Dandroid.release=true ==== In-App Payments ==== ^Method ^ Describtion ^ Methods ^ Transaction fee ^ |Native Android Play In-App Payments| Billed via Google Play Account. Service https://wallet.google.com is used. | Credit Card (Visa) \\ Debit Card (Master Card) | [[https://support.google.com/googleplay/android-developer/answer/112622?hl=en&ref_topic=15867|30%]] | |||| ==== Certificates ==== Check Signature jarsigner -verify -verbose -certs my_application.apk The Certificate can be extracted from an APK. Just Exctract the File META-INF\ADT.RSA from the apk file, since an apk can be entered as a normal zip. Then use the following code to convert the ADT.RSA to the certificate. openssl.exe pkcs7 -in ADT.RSA -print_certs -inform DER -out ADT.RSA.CER ==== Services ==== Services may run, after the app was stopped. public class MyService extends IntentService { public MyService() { super("MyService"); } @Override protected void onHandleIntent(Intent intent) { } /** * Services has to use Handlers to toast messages * @param context - the app context * @param message - the message to toast * @param handler - the handler to reuse */ public static void toastFromService(final Context context, final String message, Handler handler) { handler.post(new Runnable() { @Override public void run() { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); LOG.info("Service message: "+ message); } }); } /** Start the instance of IntentService service by sending an Intent */ public static final void start(Context context) { Intent intent = new Intent(context, MyService.class); context.startService(intent); } /** to toast stuff use the handler */ void toastMessage(final String message){ handler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); } }); } ==== Automatic Testing ==== - Create a new Test Project, as [http://developer.android.com/tools/testing/testing_android.html|stated here]] == android.test.ActivityInstrumentationTestCase2 == Use this clss to test activities. * Every method in this class will be executed as a JUnitTest. * Before every test - the method **setUp()** is executed * test methods names have to start with **"test"**, or they won't be found === Espresso - UI tests === package digital.alf.geosound; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.CoreMatchers.allOf; import android.Manifest; import android.app.Activity; import android.os.Bundle; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.test.espresso.action.ViewActions; import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.rule.GrantPermissionRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @LargeTest public class JustOpenAllWindows { @Rule public ActivityScenarioRule activityRule = new ActivityScenarioRule<>(MainActivity.class); @Rule public GrantPermissionRule mRuntimePermissionRuleFine = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION); @Rule public GrantPermissionRule mRuntimePermissionRuleCoarse = GrantPermissionRule.grant(Manifest.permission.ACCESS_COARSE_LOCATION); @Rule public GrantPermissionRule mRuntimePermissionRuleBackground = GrantPermissionRule.grant(Manifest.permission.ACCESS_BACKGROUND_LOCATION); @Test public void openPreferences() { activityRule.getScenario().onActivity(activity -> { Bundle args = new Bundle(); NavController navController = Navigation.findNavController(activity, R.id.map); navController.navigate(R.id.action_fragment_maps_to_fragment_preferences, args); }); onView(allOf(ViewMatchers.withText(R.string.dialog_preference_label), withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(isDisplayed())); onView(ViewMatchers.withText(R.string.dialog_default_onout_volume_title)) .perform(ViewActions.click()); //Click on cancel button onView(ViewMatchers.withId(android.R.id.button2)) .perform(ViewActions.click()); onView(ViewMatchers.withText(R.string.dialog_default_onout_volume_title)) .perform(ViewActions.click()); //Click on cancel button onView(ViewMatchers.withId(android.R.id.button2)) .perform(ViewActions.click()); System.out.println("done"); } } === Execution in CodeBuild === https://devops.stackexchange.com/questions/3965/configuration-of-aws-codepipeline-for-android-ci-cd ==== Snippets ==== === Preference Item with value in Summary === Declare an own xml attribute for custom Views. Add this to **res/values/attrs.xml** This attribute will contain the Mask for the summary, as known by //String.format(mask, value)// Use own attribute Implement own preference view public class PreferenceSummaryValue extends EditTextPreference { private String summaryFormat = "%s"; public PreferenceSummaryValue(Context context) { super(context); init(null, context); } public PreferenceSummaryValue(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, context); } public PreferenceSummaryValue(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, context); } private void init(AttributeSet attributes, Context context){ if(attributes != null){ TypedArray tarr = context.obtainStyledAttributes(attributes, R.styleable.PreferenceSummaryValue); String attribute = tarr.getString(R.styleable.PreferenceSummaryValue_summaryFormat); if(attribute != null) summaryFormat = attribute; tarr.recycle(); } } @Override public void setText(String text) { super.setText(text); setSummary(String.format(summaryFormat, text)); } @Override public void setSummary(CharSequence summary) { super.setSummary(summary); } public void setSummaryFormat(String summaryFormat) { this.summaryFormat = summaryFormat; } } ==== Layouts ==== === TableLayout === * Kann wirklich nur gleichmaeßige Zellen darstellen. * Kann kein RowSpan * Die Rows existieren als ein besonderes Objekt "TableRow" * Die Columns als besondere Objekte existieren nicht - es werden die CHildViews einfach gleich groß gehalten werden * Nur gleiche layouts sollten als Columns benutzt werden. TableLayout mit 1 TabeRow und LinearLayout als Table Column klappt gut. * Nutze Lieber GridLayout === GridLayout === * Has row-span which tableLayout does not have * **CAN NOT DISTRIBUTE HORIZONTAL SPACE** e.g. when you need multiple columns which occupy the parent \\{{http://i520.photobucket.com/albums/w327/schajtan/2014-04-05_11-31-28_zpse32d1857.png?600}} How to use GridLayout is described in this video: {{youtube>tsOr0QhJZaE?medium}} === LinearLayout === * Can order items in a row. * Children must have either a **given size** or a **weight** * You can also set the view with some content to **wrap_content**. Then LinearLayout will **respect the size of content** for this view when distributing space via **weight**. * CAN NOT make a child take all the **remaning space**. (Use **RelativeLayout** for that!) Achieving that is easy, when the width of children is given: \\ {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-06_09-53-05_zps41dc325e.png?300}} Trying to make the table in the middle filling the remaning space fails, because the table \\ {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-06_09-24-52_zps57daa4b2.png?300}} Example about how to use weight, width, wrap_content: {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-06_09-58-29_zps8e7b5d64.png?300}} {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-06_09-59-40_zpsd6056e19.png?300}} === FlowLayout === There is a custom FlowLayout here https://github.com/ApmeM/android-flowlayout which allows: * Automatical **breaking the line to wrap the content**, when the space is exceeded. No default layout is able to do that. Maven dependency: org.apmem.tools app 1.0 {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-08_10-14-18_zpsbca7faaa.png}} {{http://i520.photobucket.com/albums/w327/schajtan/2014-04-08_10-14-27_zpsab68927a.png}} ==== Dialogs ==== The Dialogs are quite nice in Android. They are customizable and may have a complete custom layout. // Create custom dialog object with layout R.layout.dialog final Dialog dialog = new Dialog(CustomDialog.this); // Include dialog.xml file dialog.setContentView(R.layout.dialog); // Set dialog title dialog.setTitle("Custom Dialog"); // set values for custom dialog components - text, image and button TextView text = (TextView) dialog.findViewById(R.id.textDialog); text.setText("Custom dialog Android example."); ImageView image = (ImageView) dialog.findViewById(R.id.imageDialog); image.setImageResource(R.drawable.image0); dialog.show(); Button declineButton = (Button) dialog.findViewById(R.id.declineButton); // if decline button is clicked, close the custom dialog declineButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Close dialog dialog.dismiss(); } }); However it is difficult to style the dialog completele! E.g. you can not make the DialogDivider in native DIalogs to have a custom color. == android-styled-dialogs == Allows to style the dialogs completele. |On GitHub | https://github.com/inmite/android-styled-dialogs | |On Maven | eu.inmite.android.lib android-styled-dialogs 1.1.2 apklib | {{https://github.com/inmite/android-styled-dialogs/raw/master/graphics/screenshot-small.png}} == ListView AlerDialog == ListView dialogs with custom Views in the list may be very useful! They look native too so use them if possible! To achieve that do the follwing: == 1. Create a custom ListView Item== * It should Inherit from a ViewGroup * It should have **blockdescedants** attribute set. Otherwise you wont be able to choose it in the list * Achtung: you may not use the methods, which make the items clickable, focuasble ... Otherwise the items in the list will not be clickable. //cant use this for items in lists using android.content.DialogInterface.OnClickListener item.setClickable(true); item.setFocusable(true); item.setFocusableInTouchMode(true); See this thread: http://stackoverflow.com/questions/5551042/onitemclicklistener-not-working-in-listview-android