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 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 # <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 <YOUR_ANDROID_PROJECT> ./gradlew build
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
<fc #FF0000>ACHTUNG:</fc>
xmlns:app="http://schemas.android.com/apk/res-auto"
Is described here: http://nathanael.hevenet.com/android-dev-fading-background-animation/
listView.setItemChecked(1, true);
ContentListAdapter contentListAdapter = (ContentListAdapter) listView.getAdapter(); for(int i=0; i<contentListAdapter.getCount(); i++){ View v = (View) contentListAdapter.getItem(i); Log.d("View","Child is "+v );
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.
The tool monitor.bat can be used as a Standalone logger! Details are here
Inspect the APK is done via
D:\Development\Eclipse Juno - Android\sdk\build-tools\android-4.2.2\aapt.exe dump badging theApp.apk
apktool d HelloWorld.apk ./HelloWorld
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
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
<artifactId>barcode-lib-commons-dataproject</artifactId> <name>BarcodeLibCommonsDataProject</name> <version>1.0.0</version> <packaging>apklib</packaging>
The dependency
<!-- Barcode Helper Lib --> <dependency> <groupId>de.ivu.fare.apps-13.2.PIROL</groupId> <artifactId>barcode-lib-commons-dataproject</artifactId> <version>1.0.0</version> <type>apklib</type> </dependency>
The apk is an App.
<artifactId>barcodelib2</artifactId> <version>1.0.0</version> <packaging>apk</packaging> <name>Barcode-Lib2</name>
The apklib - is the android lib project
<artifactId>barcodelib2</artifactId> <version>1.0.0</version> <packaging>apklib</packaging> <name>Barcode-Lib2</name>
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
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 <skip>false</skip>
<plugin> <groupId>com.jayway.maven.plugins.android.generation2</groupId> <artifactId>android-maven-plugin</artifactId> <version>3.2.0</version> <extensions>true</extensions> <configuration> <sdk> <platform>8</platform> </sdk> <emulator> <avd>2.3.3_API-10</avd> </emulator> <undeployBeforeDeploy>true</undeployBeforeDeploy> <assetsDirectory>${project.build.directory}/filtered-assets</assetsDirectory> <androidManifestFile>${project.build.directory}/filtered-manifest/AndroidManifest.xml</androidManifestFile> <zipalign> <skip>false</skip> <verbose>${build.verbosity}</verbose> <inputApk>${project.build.directory}/${project.artifactId}-${build.version.name}.apk</inputApk> <outputApk>${project.build.directory}/${project.artifactId}-${build.version.name}-aligned.apk</outputApk> </zipalign> </configuration> <executions> <execution> <id>zipalign</id> <phase>package</phase> <goals> <goal>zipalign</goal> </goals> </execution> </executions> </plugin>
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
clean install -Dandroid.release=true
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) | 30% |
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 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(); } }); }
Use this clss to test activities.
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<MainActivity> 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"); } }
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)
<declare-styleable name="PreferenceSummaryValue"> <attr name="summaryFormat" format="string"/> </declare-styleable>
Use own attribute
<de.ivu.eticket.app.gui.PreferenceSummaryValue android:key="deadline" android:defaultValue="12" android:title="The Deadline" app:summaryFormat="end in %s days"/>
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; } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:stretchColumns="0" > <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> <View android:background="@color/grey_light" android:layout_height="1px"></View> <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> </TableLayout> <TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> <View android:background="@color/grey_light" android:layout_height="1px"></View> <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> </TableLayout> <TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> <View android:background="@color/grey_light" android:layout_height="1px"></View> <TableRow android:id="@+id/tableRow1" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:padding="5dp" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Text"></TextView> </TableRow> </TableLayout> <TableLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <TableRow android:id="@+id/tableRow2" android:layout_width="wrap_content" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" android:text="Text" android:textAppearance="?android:attr/textAppearanceMedium" /> </LinearLayout> </TableRow> </TableLayout> </TableRow> <!-- display this button in 3rd column via layout_column(zero based) --> <!-- display this button in 2nd column via layout_column(zero based) --> </TableLayout> </LinearLayout>
How to use GridLayout is described in this video:
Achieving that is easy, when the width of children is given:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:layout_width="50dip" android:layout_height="100dip" android:background="#cc0000"/> <TableLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TableRow> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#aaaa00"/> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#00aa00"/> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#aaaa00"/> </TableRow> <TableRow> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#00aa00"/> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#aaaa00"/> <ImageView android:layout_width="50dip" android:layout_height="50dip" android:background="#00aa00"/> </TableRow> </TableLayout> <ImageView android:layout_width="50dip" android:layout_height="100dip" android:background="#cc0000"/> </LinearLayout>
Trying to make the table in the middle filling the remaning space fails, because the table
Example about how to use weight, width, wrap_content:
There is a custom FlowLayout here https://github.com/ApmeM/android-flowlayout which allows:
Maven dependency:
<dependency> <groupId>org.apmem.tools</groupId> <artifactId>app</artifactId> <version>1.0</version> </dependency>
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.
Allows to style the dialogs completele.
On GitHub | https://github.com/inmite/android-styled-dialogs |
On Maven | <dependency> <groupId>eu.inmite.android.lib</groupId> <artifactId>android-styled-dialogs</artifactId> <version>1.1.2</version> <type>apklib</type> </dependency> |
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:
//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
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="blocksDescendants" android:orientation="vertical" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/rightCenteredContainer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:orientation="vertical" > <Button android:id="@+id/buttonChooseTrip" style="@style/Green.Button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="true" android:height="@dimen/btn_trip_size" android:text=">" android:width="60dp" /> </LinearLayout> <TableLayout android:layout_width="wrap_content" android:layout_height="60dp" android:layout_alignParentLeft="true" android:layout_toLeftOf="@id/rightCenteredContainer" android:gravity="center_vertical" android:padding="5dp" > <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/dialogTripsDestinations" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent" android:text="München (ZOB) - Augsburg (Zentrum)" android:textStyle="bold" /> </TableRow> <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/dialogTripsTimes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent" android:text="16.55 - 16.10.2014" android:textSize="12dp" /> </TableRow> </TableLayout> </RelativeLayout> </LinearLayout>
import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class DialogueTripItem extends LinearLayout{ Context mContext; TextView dialogTripsDestinations; TextView dialogTripsTimes; Button buttonChooseTrip; public DialogueTripItem(Context context) { super(context); init(context); } public DialogueTripItem(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public DialogueTripItem(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context){ this.mContext = context; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.dialog__select_current_trip_item, this, true); dialogTripsDestinations = (TextView) findViewById(R.id.dialogTripsDestinations); dialogTripsTimes = (TextView) findViewById(R.id.dialogTripsTimes); buttonChooseTrip = (Button) findViewById(R.id.buttonChooseTrip); } public void setDestinations(String dialogTripsDestinations){ this.dialogTripsDestinations.setText(dialogTripsDestinations); } public void setDialogTripsTimes(String dialogTripsTimes){ this.dialogTripsTimes.setText(dialogTripsTimes); } public Button getButton(){ return buttonChooseTrip; } }
ListAdapter adapter = new ListAdapter(); final DialogueTripItem item = new DialogueTripItem(activity); adapter.content.add(item); AlertDialog.Builder builder = new IvuColorsAlertDialogBuilder(activity); builder.setAdapter(adapter, new android.content.DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(getApplicationContext(), "Click "+which, Toast.LENGTH_LONG).show(); } }); builder.setTitle("Title"); builder.setIcon(activity.getResources().getDrawable(R.drawable.icon_xhdpi)); builder.setNegativeButton("Cancel", cancelListener);
An APK installation may be started programmatically. The use case was to put a second (library) application into the assets folder of the main applicaiton. Install the library application on demand.
Important:
private void installApk() { Intent intent = new Intent(Intent.ACTION_VIEW); // the name of the apk in the assests folder String assetFileName = "CommonBarcode115.apk"; // location where the app will be temporary copied to File cacheFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"//Android//data//de.example.maintestapp"); // the ful path to the cache file File fileCache = new File(cacheFolder, assetFileName); // copy the file to the cache try { // needs android.permission.WRITE_EXTERNAL_STORAGE fileCache.getParentFile().mkdirs(); fileCache.createNewFile(); // delete old file if (fileCache.exists()) { fileCache.delete(); } copyAsset(assetFileName, cacheFolder); } catch (Exception e) { Toast.makeText(getApplicationContext(), "Ein Fehler beim kopieren", Toast.LENGTH_LONG); } if (!fileCache.exists()) { // if (!assetExists(this.getAssets(), assetFile)) { Toast.makeText(getApplicationContext(), "APK not found", Toast.LENGTH_LONG).show(); return; } intent.setDataAndType(Uri.fromFile(fileCache),"application/vnd.android.package-archive"); startActivity(intent); } private void copyAsset(String assetFileName, File copyToFolderPath) { AssetManager assetManager = getAssets(); String[] files = null; try { files = assetManager.list(""); } catch (IOException e) { Toast.makeText(getApplicationContext(), "Failed to load assets", Toast.LENGTH_LONG).show(); } for(String filename : files) { if(!filename.equals(assetFileName)){ continue; } InputStream in = null; OutputStream out = null; try { in = assetManager.open(filename); File outFile = new File(copyToFolderPath, filename); out = new FileOutputStream(outFile); copyFile(in, out); in.close(); in = null; out.flush(); out.close(); out = null; } catch(IOException e) { Toast.makeText(getApplicationContext(), "Failed to copy asset file", Toast.LENGTH_LONG).show(); } } } private void copyFile(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } } private static boolean assetExists(AssetManager assets, String name) { try { // using File to extract path / filename // alternatively use name.lastIndexOf("/") to extract the path File f = new File(name); String parent = f.getParent(); if (parent == null) parent = ""; String fileName = f.getName(); // now use path to list all files String[] assetList = assets.list(parent); if (assetList != null && assetList.length > 0) { for (String item : assetList) { if (fileName.equals(item)) return true; } } } catch (IOException e) { // Log.w(TAG, e); // enable to log errors } return false; }
To Capture HTTP Communication of Android, to simulate slow connection etc. use the following Tools:
In your virtual device,
In Fiddler,
Use the Connectify, to connect your Phone with the Net via the PC with Fiddler.
Configure FIddler as described above.
Fragments can be defined in XML by using a
<fragment android:id="@+id/fragmentMainContent" class="com.amberfog.mapslidingtest.app.FragmentMainContent" android:layout_width="match_parent" android:layout_height="match_parent"/>
The minimal fragment class looks as following
public class FragmentMainContent extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_maincontent_layout, container,false); } }
where the fragment_maincontent_layout may look as whatever you like. For example:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <GridLayout android:layout_width="match_parent" android:layout_height="match_parent" android:columnCount="1" > <AnalogClock android:id="@+id/analogClock1" android:layout_column="0" android:layout_gravity="center" android:layout_row="0" /> </GridLayout> </LinearLayout>
// ACHTUNG: when using setRetainInstance(true) on a fragment to restore it's vars on configchange - check whether the fragment already exists before recreating it this.fragmentDetails = (Fragment1Details) fragmentManager.findFragmentByTag("fragmentTag"); if(this.fragmentDetails==null) { // create the fragment here and tag it with "fragmentTag" }
Class | Usage | Details |
---|---|---|
FragmentManager | Activity.getFragmentManager() | Used to control the Fragments |
FragmentTransaction | getFragmentManager().beginTransaction() | Is able to create, update, remove fragments |
Fragments may be either created in xml or programmatically.
In XML
<fragment android:id="@+id/fragmentId" android:layout_width="wrap_content" android:layout_height="wrap_content"> </fragment>
Programmatically. Programmatically fragment operations are done via FragmentManager which create FragmentTranscation
ACHTUNG: The fragment must live inside a ViewGroup R.id.fragmentContainer ist such a ViewGroup here
// get Fragment manager FragmentManager fragmentManager = getFragmentManager(); // ACHTUNG: when using setRetainInstance(true) on a fragment to restore it's vars on configchange - check whether the fragment already exists before recreating it this.fragmentDetails = (Fragment1Details) fragmentManager.findFragmentByTag("fragmentDetails"); if(this.fragmentDetails==null) { // start a transaction FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); // create a fragment this.fragmentDetails = new Fragment1Details(); // set the id to the fragment // The Fragment should not be recreated when the Application is killed this.fragmentDetails.setRetainInstance(true); // add the fragment to the view fragmentTransaction.add(R.id.fragmentContainer, this.fragmentDetails, "fragmentDetails"); // //make transaction reversable by undoing the action on the fragmentmanager // fragmentTransaction.addToBackStack(null); // commit the transaction fragmentTransaction.commit(); // init the value from model of fragmentDetails. myActivityModel.setText(myActivityModel.getText()); }else{ Log.d(TAG,"Fragment already exists. Do not recreate it!"); }
To scroll the View continous prgrammatic scrolling.
To scroll continously the view has to be scrolled by a little delta in a regular time period.
The period of time should be nearly 24 Frames per second, which is every 41 ms.
The distance-change in px per ms depend from the speed you wish to achieve.
0.1 - 0.2 PX per MS | Slow movement |
0.4 - 0.7 PX per MS | Middle speed |
1 PX - 1.4 PX per MS | Fast speed |
12PFS | Seems to be not enough eince you can see the picture jerking | ![]() |
24PFS | Seems to be ok | ![]() |
36PFS | No difference to 24FPS | ![]() |
COde to test differend scroll speeds
return new Thread(new Runnable() { @Override public void run() { // scroll time int scrollTimeMs = 4000; // SPEED IN PX / MS, IF THE IMAGE WULD MOVE CONTIONOUSLY double pxPerMsStart = 0.2; double pxPerEnd = 0.8; double pxPerMsChangeBy = 0.1; // FPS int fpsStart = 24; //per sec int fpsEnd = 24; int fpsChangeBy = 12; // iterate pause time for(double scrollspeedPxPerMs = pxPerMsStart; scrollspeedPxPerMs <=pxPerEnd; scrollspeedPxPerMs +=pxPerMsChangeBy){ // iterate steps for(int fps=fpsStart; fps<= fpsEnd; fps+=fpsChangeBy){ // pauses in ms are computed from fps pauseMs = 1000/fps; /* the speed is given for the case, when the image moves contingously because there is a redraw pause between movements - we have to multiply the pause in MS with the speed to know to which amount the image has to be moved after every pause */ steppx = (int) Math.round((double)pauseMs * scrollspeedPxPerMs); // timestemp for next iteration timestampWhenToStop = System.currentTimeMillis() + scrollTimeMs; // annouce settings final String message = String.format("Step by %s px, every %s ms - FPS: %s Scroll by %s px per MS", steppx, pauseMs, fps, scrollspeedPxPerMs ); Log.d("stepscroll", message); // jump to the top ViewGroupAnimatedActivity6.this.runOnUiThread(new Runnable() { public void run() { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); scrollView.setSmoothScrollingEnabled(false); scrollView.scrollTo(0, 0); scrollView.setSmoothScrollingEnabled(true); } }); // scroll to the bottom boolean stopIteration = false; while (!stopIteration){ ViewGroupAnimatedActivity6.this.runOnUiThread(new Runnable() { public void run() { scrollView.smoothScrollBy(0, steppx); } }); if(timestampWhenToStop < System.currentTimeMillis()){ stopIteration = true; continue; } try { Thread.sleep(pauseMs); } catch (InterruptedException e) { e.printStackTrace(); } if(!isRunning){ return; } } }// for fps } // for pxPerEnd isRunning = false; } }); }
To execute gradle tasks - a gradle wrapper file is used. The wrapper is a shell / batch script, which downloads the gradle and so does not require gradle to be installed previously. The wrapper uses it's own gradle version The name of the file is gradlew.bat
To generate the gradle wrapper use gradle and add the wrapper :
gradle wrapper
aidl
- all aidl have to be located in th same packages - can pass Parcels or primitives - implement aidl to be able to retrieve a common interface to communicate between different applicaitons, which are different processes
public void onServiceConnected(ComponentName name, IBinder service) { IServiceReadTicketBinder serviceReadTicketBinder = IServiceReadTicketBinder.Stub.asInterface(service); // IServiceReadTicketBinder serviceReadTicketBinder = (IServiceReadTicketBinder)service; // not allowed when retriveing service from another application. service var is of type proxy
- add in out inout as stated here http://www.app-solut.com/blog/2011/05/using-self-defined-parcelable-objects-during-an-android-aidl-rpc-ipc-call/ - use Bundle.class to exchange data with the Service. It provides possibility to restore the stored values typesafe. Bundle is like a Hashmap which can store different types of values result.getString(“Test”)
The bare minimum in code
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final ViewDataBinding binding = DataBindingUtil.inflate( inflater, R.layout.fragment_poidialog, container, false); final View view = binding.getRoot(); return view; }
The binding in xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" > <include layout="@layout/layout_common" app:passedText="@{@string/app_name}" // here we pass any String /> </LinearLayout> </layout> <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" > <data> // declare fields <variable name="passedText" type="String"/> </data> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{passedText}"/> //set field to your view. </layout>
The text size in android is controlled by “TextAppearance”
Material Design provides 13 type “styles” that are applied to all the text in your app. Each of these have a design term (eg. “Body 1”) along with a corresponding type attribute that can be overridden in your app theme (eg. textAppearanceBody1). There are default “baseline” values (text size, letter spacing, capitalization, etc.) for each style.
At the end, to express which size the text in your custom view should have instead of influencing “textSize” directly which will lead to difficulties with consistancy across app and consistancy with android native textSizes
you should set “android:textAppearance=” on your view.
and the value should be one of androids own textAppearance attributes:
The same attributes are used by android too, so your view will be consistant with android natives
In your someAppPartLayout.xml
<!-- My title in my app view --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title" android:textAppearance="?attr/textAppearanceHeadline4" />
In your theme, maybe defined in your styles.xml you can change single android attributes.
In my theme, which is defined in this style - overriding the single attributes “textAppearance*” referencing MY styles, which can interfere and override the default values for textAppearance.
Remark: Those textAppearance* attributes are also used in android native views, like buttons see https://material.io/blog/android-material-theme-type. So overriding those in a theme - changes the behavior also for Android native elements
THose attributes are not just strings, but are introduced as typed attributes in attrs.xml with format=“reference”. format=“reference” means, that one can take a “<style></style>” element as a value. In the value-style element - the items a la android:textSize are expected (convention).
USAGE of attributes The attribute can be USED in views, by refering to the attribute in the view e.g.
android:textAppearance="?attr/textAppearanceHeadline4" />
In your styles.xml pointing textAppearanceHeadline1 attribute of type “reference” to my own style.
The style inherits from androids own style but I still can override some values in my inherited style.
<!-- Base application theme. --> <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="textAppearanceHeadline1"> @style/TextAppearance.Headline1 </item>
THe value of an overridden attribute textAppearance* is a style
in your styles.xml parallel to the AppTheme - introduce styles, to refer from attributes.
<!-- here I can change attributes of the STYLES. by Inheriting from "TextAppearance.MaterialComponents.*" I inherit all the native properties but get the possibility to override them here --> <style name="TextAppearance.Headline1" parent="TextAppearance.MaterialComponents.Headline1"> <item name="android:textSize">96sp</item> </style> <style name="TextAppearance.Headline2" parent="TextAppearance.MaterialComponents.Headline2"> <item name="android:textSize">60sp</item> </style> <style name="TextAppearance.Headline3" parent="TextAppearance.MaterialComponents.Headline3"> <item name="android:textSize">48sp</item> </style> <style name="TextAppearance.Headline4" parent="TextAppearance.MaterialComponents.Headline4"> <item name="android:textSize">34sp</item> </style> <style name="TextAppearance.Headline5" parent="TextAppearance.MaterialComponents.Headline5"> <item name="android:textSize">24sp</item> </style> <style name="TextAppearance.Headline6" parent="TextAppearance.MaterialComponents.Headline6"> <item name="android:textSize">20sp</item> </style> <style name="TextAppearance.Body1" parent="TextAppearance.MaterialComponents.Body1"> <item name="android:textSize">16sp</item> </style> <style name="TextAppearance.Body2" parent="TextAppearance.MaterialComponents.Body2"> <item name="android:textSize">14sp</item> </style>