How to make sure that your Android menu icon is displayed whatever the background color is

Wow, that’s a long title ūüėČ

The problem with background colors

Today I downloaded the Action Bar Icon Pack¬†from the official Android develop site and I used one of the icons (“2_action_help.png”) in the menu of one of my applications.

In the icon pack, each icon comes in 2 different colors: one for the “holo dark” theme and one for the “holo light” theme.¬†I chosed the one from the “holo dark” theme, with this menu.xml:


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item
      android:id="@+id/about_menu_2"
      android:icon="@drawable/action_help_holo_dark"
      android:title="About"/>
</menu>

…and ended up with this result in the Android emulator:

about_emulator

So far so good. Then I deployed my application on a real device (same Android version as the emulator) and ended up with this:

about_real

What the f… ?

Yes indeed: on my real device, the menu has a white background, which means that the icon cannot be seen (white on white). Using the “holo light” icon instead fixes the problem, but in that case the icon is not displayed on the emulator (where the menu has a black background).

What actually happens is that the real device manufacturer (SAMSUNG for me), can choose to customize the Android default theme and in that case, it will be different from the one used by your emulator.

How to solve this issue

It turns out that this is a common problem and there are lots of blog posts to talk about this. 4 solutions are usually suggested:

  1. Use a set of icons that will work with all background colors as suggested here.
  2. Embeds different icons in your application and dynamically select the one that will best match the background color.
  3. Customize the menu by specifying your own background/text color as suggested here.
  4. Use the builtin icons

Let’s talk about option #4: using the Android builtin icons

Using Android built-in icons

Android comes with a bunch of built-in drawables that are available in “android.R.drawable”.

Jeremy Logan created a great website that displays all the built-in drawables for all Android version, check out androiddrawables.com.

So basically, if I want to use the built-in “help” icon, I need to do this:


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item
      android:id="@+id/about_menu_1"
      android:icon="@android:drawable/ic_menu_help"
      android:title="About"/>
</menu>

Result on the emulator:

about_emulator_2

Result on the real device:

about_real_2

Why does it work ? Because if the real device manufacturer decides to change the default Android theme… then he also needs to change the built-in icons !

Some people will tell you that referencing these built-in icons is a bad idea and that you should copy them in your local drawable folders. In fact, the official Android documentation states that:

Warning: Because these resources can change between platform versions, you should not reference these icons using the Android platform resource IDs (i.e. menu icons under android.R.drawable). If you want to use any icons or other internal drawable resources, you should store a local copy of those icons or drawables in your application resources, then reference the local copy from your application code. In that way, you can maintain control over the appearance of your icons, even if the system’s copy changes.

The problem with this “recommanded” approach is that we end up with the initial problem ! Anyway, in case you want to do this, the png files for the built-in icons are here:¬†[ANDROID_SDK_HOME]/platforms/android-[VERSION]/data/res

Creating your own drawables

As an added bonus, Google provides a cool online icon generator tool, you may want to check it out: Android Asset Studio.

Advertisements

How to know which activities are running in Android

An Android process does not correspond to an Android activity, which means that even if there is a running process corresponding to your application (I’m talking about the processed listed by the “ps” command), you cannot use this information to know how many of your activities are still alive.

OK, so if you cannot use “ps” command, how can you know which activities are running ?

Well, you need to use the “dumpsys activity” command.

To demonstrate this, I’m going to use a sample application:

  • the package name in the AndroidManifest.xml is fr.lk.notes (i.e: package=”fr.lk.notes”)
  • the main activity is named fr.lk.notes.activity.ViewNotes
  • there is a second activity named¬†fr.lk.notes.activity.CreateOrEditNote

Exercice #1: we launch the application and then exit it by clicking on the BACK button

This will invoke ViewNotes.onDestroy(), which means that the main activity will be killed.

Yet, the “ps” command shows that the application process is still running:


C:\android-sdk\platform-tools>adb shell ps
USER PID PPID VSIZE RSS WCHAN PC NAME
root 1 0 404 280 c0274189 08054b16 S /init
[SNIP]
u0_a43 1635 793 216720 31232 ffffffff b802c157 S fr.lk.notes
[SNIP]

Now if we launch the “dumpsys activity” command, and if we look at the “Running activities” section, we can see that there is no reference to any of our activites, which means that they have all been destroyed:


C:\android-sdk\platform-tools>adb shell dumpsys activity
[SNIP]

Running activities (most recent first):

TaskRecord{b3e1dc08 #2 A com.android.launcher U 0}
 Run #0: ActivityRecord{b3e04590 com.android.launcher/com.android.launcher2.Launcher}

[SNIP]

Exercice #2: we launch the application

This puts the main “ViewNotes” activity in a running state and we can see it in the dumpsys output:


[SNIP]

Running activities (most recent first):

TaskRecord{b3eb03a8 #6 A fr.lk.notes U 0}
 Run #1: ActivityRecord{b3c1f2d8 fr.lk.notes/.activity.ViewNotes}
 TaskRecord{b3e1dc08 #2 A com.android.launcher U 0}Run #0: ActivityRecord{b3e04590 com.android.launcher/com.android.launcher2.Launcher

[SNIP]

Exercice #3: we launch the application and navigate to the second activity

This leaves the main activity in a “stopped” state and dumpsys shows both activities:


[SNIP]

Running activities (most recent first):

TaskRecord{b3eb03a8 #6 A fr.lk.notes U 0}
 Run #2: ActivityRecord{b3ee1ce0 fr.lk.notes/.activity.CreateOrEditNote}
 Run #1: ActivityRecord{b3c1f2d8 fr.lk.notes/.activity.ViewNotes}
 TaskRecord{b3e1dc08 #2 A com.android.launcher U 0}Run #0: ActivityRecord{b3e04590 com.android.launcher/com.android.launcher2.Launcher}

[SNIP]

Laurent KUBASKI

Why Activity.onDestroy() and Activity.onRestoreInstanceState() are not always invoked

First, the facts:

  1. Activity.onStop() is not always followed by Activity.onDestroy() since Android can decide to kill your application just after invoking Activity.onStop()
  2. Activity.onSaveInstanceState() is not always followed by Activity.onRestoreInstanceState()

1. When is Activity.onDestroy() invoked ?

First, check the¬†Android activity lifecycle: as you can see, Android can kill your application without invoking onDestroy() if “an app with higher priority needs memory“.

In fact, the javadoc for onDestroy() also states that “There are situations where the system will simply kill the activity’s hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away“.

I wrote a simple application containing only one (main) activity and used a couple of scenarios to have a better understanding of when onDestroy() was called:

Scenario #1-1: HOME key is hit while the activity is running

  • Android invokes onSaveInstanceState() -> onPause() -> onStop()
  • You are now back on the HOME screen
  • When the application is relaunched, Android invokes onCreate() -> onStart() -> onResume()

Note: onCreate() may actually not be invoked when the application is started again because it’s up to the Android OS to decide whether or not to kill your activity just after invoking onStop(). So if onCreate() is not invoked, this simply means that your activity was still in the background in a “stopped” state.

Scenario #1-2: BACK key is hit while the activity is running

  • Android invokes onPause() -> onStop() -> onDestroy()
  • You go back to the screen you were before launching the application
  • When the application is relaunched, Android invokes onCreate() -> onStart() -> onResume()

Note: this time, onCreate() is always invoked because hitting the BACK key destroys your activity.

Scenario #1-3: screen orientation changes while the activity is running (CTRL-F12 in the Android emulator)

  • Android invokes onSaveInstanceState() -> onPause() -> onStop() -> onDestroy()
  • It then invokes: onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()

Scenario #1-4: another application is launched while the activity is running (click on the phone button in the Android emulator)

  • Android invokes onSaveInstanceState() -> onPause() -> onStop()
  • If you now hit BACK to come back to your application: onStart() -> onResume()

 

2. When is Activity.onRestoreInstanceState() invoked ?

If you check again the various scenarios above, you will see that onSaveInstanceState() is indeed not always followed by onRestoreInstanceState().

Why ?

Because there are only 2 scenarios where onSaveInstanceState() is followed by onRestoreInstanceState():

  1. The screen orientation changes (see scenario #1-3)
  2. Another activity gets the focus and then Android decides to kill the previous one (possibly because the device is low on resources)

To test#2 using the Android emulator, you need to do this:

  • Launch your application so that your activity is running and has the focus
  • Launch another application (for ex: click on the phone button in the Android emulator to launch the dialer application)
  • Launch an ADB shell and use the “ps” command to kill your application
  • From the dialer application, hit the BACK button
  • Android will invoke onCreate() -> onStart() -> onRestoreInstanceState -> onResume()

 

Laurent KUBASKI

Running Android unit tests in Intellij

It turns out that running Android unit tests in Intellij is not as easy as creating a unit test in the [ANDROID_MODULE_HOME]/src folder.

You instead need to create a new “Android test module” inside your main module and run your tests from there:

If you choose the default options, your new test module (“myAndroidTestProject”) will be created inside the main module (“myAndroidProject”):

This test module has a dependency on the main module:

And the nice thing is that Intellij automatically creates an Android unit test configuration that automatically executes all the unit tests in this new test module:

From there, you can create your unit tests in this new module. Here is a simple one:


public class MyAndroidTestCase extends AndroidTestCase {

public void test1() throws IOException, JSONException {
 Log.d(this.getClass().toString(), (">> test1"));
 Context ctx = getContext();
 FileOutputStream fos = ctx.openFileOutput("test.dat", Context.MODE_PRIVATE);
 fos.write("hello the world".getBytes());
 fos.close();
 assertTrue(Arrays.equals(new String[]{"test.dat"}, ctx.fileList()));
 ctx.deleteFile("test.dat");
 Log.d(this.getClass().toString(), ("<< test1"));
 }
}

Note that Android unit tests cannot be directly ran from Intellij: you need to run them from the emulator (or from a real device):

About the Android toolbox binary

1. What is Android toolbox ?

While browsing the filesystem of my Android emulator, I was surprised to discover that most of the items in the /system/bin folder are actually symlinks to the toolbox binary:


C:\android-sdk\platform-tools>adb shell
# ls -l /system/bin
ls -l /system/bin
[SNIP]
lrwxr-xr-x root shell 2012-08-23 07:01 cat -> toolbox
-rwxr-xr-x root shell 129416 2012-08-23 07:00 check_prereq
lrwxr-xr-x root shell 2012-08-23 07:01 chmod -> toolbox
lrwxr-xr-x root shell 2012-08-23 07:01 chown -> toolbox
[SNIP]
-rwxr-xr-x root shell 181002 2012-08-23 07:01 toolbox
[SNIP]
#

So… what is this toolbox binary ?

Toolbox is like a “swiss army knife”: it allows you to run multiple commands from a single binary. So instead of having different binaries for each standard Unix command (like ls¬†or¬†rm), you can have a single binary (toolbox) that can execute all these commands.

To use it, simply execute “toolbox XX” instead of “XX”. So to list the content of the “/system/bin” folder, I could have used “toolbox ls -l /system/bin”:


# toolbox ls -l /system/bin
toolbox ls -l /system/bin
[SNIP]
lrwxr-xr-x root shell 2012-08-23 07:01 cat -> toolbox
-rwxr-xr-x root shell 129416 2012-08-23 07:00 check_prereq
lrwxr-xr-x root shell 2012-08-23 07:01 chmod -> toolbox
lrwxr-xr-x root shell 2012-08-23 07:01 chown -> toolbox
[SNIP]
-rwxr-xr-x root shell 181002 2012-08-23 07:01 toolbox
[SNIP]
#

Check the toolbox git repository to see which commands can be used.

A popular alternative to toolbox is BusyBox, which provides even more functionnalities.

2. How do toolbox symlinks work ?

As explained above, the “ls” command is actually a symlink that references toolbox:


# ls -l /system/bin/ls
ls -l /system/bin/ls
lrwxr-xr-x root shell 2012-08-23 07:01 ls -> toolbox

The question is: since we have dozens of symlinks that reference toolbox…how does toolbox know which symlink was originally executed ?

It turns out that is actually easy to know, and I’m going to write a Linux bash script showing how you can reproduce this behavior.

First, I have a “script.sh” script and 2 symlinks (named “cmd1” and “cmd2”) that reference this script:


[laurent@localhost toolbox_example]$ ll
total 4
lrwxrwxrwx. 1 laurent laurent 9 Nov 22 11:18 cmd1 -> script.sh
lrwxrwxrwx. 1 laurent laurent 9 Nov 22 11:18 cmd2 -> script.sh
-rwxrwxr-x. 1 laurent laurent 211 Nov 22 11:22 script.sh

And here is the content of my script:


[laurent@localhost toolbox_example]$ cat script.sh
#!/usr/bin/bash
command=`basename $0`
case "$command" in
cmd1) echo "executing cmd1 with args=[$*]"
 ;;
cmd2) echo "executing cmd2 with args=[$*]"
 ;;
*) echo "Invalid option"
 ;;
esac

Let’s see this in action:


[laurent@localhost toolbox_example]$ ./cmd1 arg1
executing cmd1 with args=[arg1]
[laurent@localhost toolbox_example]$ ./cmd2 arg2
executing cmd2 with args=[arg2]

Of course, this concept can be easily ported to C/C++.

Any questions ?

Laurent KUBASKI

Android documentation in CHM format

In my previous post, I explain various methods to retrieve the Android API documentation.

The thing is, I’ve always preferred to work with documentations in Windows CHM format. Why ? Because:

  • CHM documents come with a built-in menu.
  • CHM documents come with an index.
  • CHM documents come with a search feature.

The other thing is that I also always wanted to create my own project on code.google.com… so after hours and hours of HTML scraping, here it is, the full Android documentation in CHM format.

(btw if you are using Linux, there are multiple CHM viewers available for your platform)

Laurent KUBASKI

Downloading the Android javadoc

There are 4 ways to get access to the Android javadoc:

1. Online access

The most obvious solution: the online documentation is here.

 

2. Downloading it using the Android SDK Manager

This one is not so obvious since you would expect the javadoc for the 2.3.3 API to be located under the “Android 2.3.3 (API 10)” section.

It is not: you need to download the documentation for the latest Android API version. In my case, it is Android 4.1:

This essentially downloads a copy of the online documentation to [ANDROID_SDK_HOME]/docs. From there, you can filter by API level exactly like in the online documentation.

 

3. Downloading the javadoc from mavencentral

That’s the only way to download an “official” javadoc.

Go to Maven Central and search for “com.google.android” (or click here) and you’ll get access to tha javadoc for each release. For example, the javadoc for Android 2.3.3 is here.

As an added bonus, this page will tell you how to determine the API level corresponding to your Android version.

 

4. Downloading the javadoc in CHM format

Not really an alternative option, but rather an alternative format that makes browsing more easy: http://code.google.com/p/android-chm-documentation/

Laurent KUBASKI