I have recently spent some time getting all the appropriate drawable assets together for the notifications in an app, including support of Lollipop, Wear, and pre-Lollipop devices. Unfortunately, it took a lot of digging and a bit of trial and error to get everything right, so I figured I would document everything in one place in the hopes that it helps someone else (or, maybe it helps me next time I’m trying to remember how I did all this).

TL; DR

Devices

Size

Padding

Color(s)

Density

Small Icon

phone, tablet

24×24 dp

1 dp

#ffffff, transparent

any

Action Icon

phone, tablet

32×32 dp

4 dp

#ffffff, transparent

any

Action Icon

wear

64×64 dp

8 dp

#ffffff, transparent

hdpi

Background

wear

400×400 px or 600×400 px

any

any

nodpi

Small Icon

The small icon is in the lower right corner of a notification on Ice Cream Sandwich through KitKat and gets put inside a circle and brought over to the left on Lollipop. One version of the icon will be equally suited for use on all of those platform versions. The icon should be 24×24 dp with about 1 dp of padding on each side. It should be pure white (#ffffff) on a transparent background only, using the alpha channel as needed for blending. Provide as many different densities (e.g. xhdpi, xxhdpi, etc.) as needed.

Action Icon

Action icons are used with any actions that you may add to your notification, for example the Delete or Archive button that GMail adds to a new message notification. These icons should follow the Action Bar icon sizing parameters: 32×32 dp with 4 dp of padding on each side. These should also be only pure white (#ffffff) on a transparent background, using the alpha channel as needed for blending. These icons will be automatically re-colored on Lollipop devices, no additional assets required. Provide as many different densities (e.g. xhdpi, xxhdpi, etc.) as needed.

For Wear, you will want alternative versions of your action icons or you will end up with your icons being scaled up significantly and looking blurry on Wear devices. The Wear versions should be double the size of the phone and tablet version: 64×64 dp. However, currently you only need to provide hdpi versions for Wear. So, ultimately, you just need an hdpi asset that is 96×96 px. You must also name these Wear versions differently so that they do not collide with your phone/tablet version.

Background

The Background is used only for Wear devices. It should be 400×400 px or 640×400 px, density independent. It can be full color. A 640×400 version will parallax more than a 400×400 version, which may not parallax at all, depending on the device being used. Since they are density independent, these assets should be placed in your drawable-nodpi directory.

On initial glance, the layout_alignBaseline attribute that can be added to children of a RelativeLayout seems like a handy little tool that is used just as easily as the more common layout_alignTop and layout_alignBottom. The good news is that if you’ve only got a couple of views, it works great! The not so good news is that if your layout gets just a bit more complicated, things fall apart in a hurry.

Let’s say you have a nice design that you need to implement:

The desired result.

I’m not here to argue about what layout is the absolute best for this situation. But, for argument’s sake, let’s say you decide you can easily build this with a RelativeLayout with three children. Nice, flat hierarchy. You might end up with something like this:

So, “United States of America” lines its baseline up with the baseline of “MI” and then “Michigan” sits on top. Perfect. But then you look at the rendered output of that layout:

RelativeLayout fails challenge.

So, what went wrong? Did you mess up one of the layout attributes? Turns out it is actually a limitation of RelativeLayout. If you dig into the source (line 519), you will notice that baseline alignment is performed after all other vertical alignment has already been performed. In other words, “Michigan” is lined up above “United States of America” when it is still in its default position at the top of the RelativeLayout container. Then, later on, “United States of America” is re-aligned with the baseline of “MI,” leaving “Michigan” just out of view, above the top of the RelativeLayout.

One potential solution to this problem is to wrap the two smaller TextViews in a LinearLayout that can then be properly baseline aligned with the larger TextView on the left. For example:

Voila! We get the desired result at the expense of an extra level in the View hierarchy. Oh, and did you notice that neat, relatively rarely used attribute on the LinearLayout? In case you missed it, baselineAlignedChildIndex is a cool little trick to tell the LinearLayout which of its children should be considered when lining up the entire LinearLayout with another View. Very handy!

You probably already know that when sending an SMS you have a relatively small number of characters available for each message, somewhere in the 140-160 character range. What you may not know (particularly if English is your native language) is that the character limit can be cut down to just 70 if you include Unicode characters in your message.

This problem has bitten us in two different apps over the past year or so. A sharing message, carefully crafted to fit well within a 140-character limit for simple sharing via SMS (or Twitter, or some other medium) while developing in English was being chopped into two or even three separate messages in other languages.

A simple ASCII-only message of about 140 characters.

Let’s just ignore additional issues that can arise from the fact that many carriers have decided to handle text messages in their own, sometimes proprietary, way and the additional issues that come from those carriers trying to interoperate with each other and instead focus on the SMS standard which seems to apply in lots of situations. SMS was designed to have a data payload of 140 octets (140 8-bit bytes). So, depending on what character set you are using, you can squeeze 160 characters (7-bit ASCII), 140 characters (8-bit GSM), or 70 characters (16-bit Unicode) into that payload. Pretty quickly, we can see why we might get truncated messages in languages that use characters outside the 7-bit ASCII alphabet. So, we might expect major issues in Russian or Japanese, but maybe not so much in French or Italian.

Just one Unicode character completely changes the result.

But even text that appears to be harmless 7-bit ASCII at first glance can cause issues. Just one Unicode character in a message will cause the entire message to be sent in Unicode, slicing the character count in half. And that single Unicode character doesn’t have to be Russian, Japanese, Chinese, Greek, Tagalog or some other obvious standout. Something as simple as a curly apostrophe (’) instead of a straight apostrophe (‘) can break your messages and lead to much confusion and hair pulling. Even more difficult to diagnose is the non-breaking space (you know, good old &nbsp;). We have received translations with non-breaking space Unicode characters hiding innocently alongside normal space characters, basically impossible to detect without a specific script checking for that character.

So, the moral of the story is this: if you have strings that you are getting localized and that you know are supposed to be used in an SMS, make sure you know exactly which characters are going into the message so that you can make proper adjustments for character counts.

In an effort to keep my View hierarchies simple, I have sometimes found myself using a little trick to layout Views on one half of a RelativeLayout container. The trick is to use a zero-size, centered View and then align your other Views based on that. Something as simple as this:

Cool! However, there is a small caveat to this. If the RelativeLayout container has an inexact size (e.g. it has defined height/width as wrap_content), this layout trick will not work as expected. The TextView would end up aligned against the left edge of the container instead of in the right half of the container.

If we dig into the source code for RelativeLayout, we can find the cause of this problem in the onMeasure method. In a first pass through the views in the RelativeLayout, centered views are placed in the center of the container. However, if the RelativeLayout has a width/height that is wrap_content, centered views are placed along the left edge of the container. This is because we don’t yet know the width/height of the RelativeLayout container as we must wait for all of the child views to be measured. At the very end of the onMeasure method, child views that have requested to be centered are corrected since we now know the height/width of the RelativeLayout container. Unfortunately, child views that depend on the positioning of these centered views are not further adjusted. So, the child views that are dependent upon the positioning of a centered view end up being positioned based upon the centered view’s location when it was originally aligned against the left edge of the container.

Let’s see how this might look:

The mess on the left is caused by overlapping TextViews that were supposed to have been aligned (and not overlapped) based on a centered View.

There are several ways to fix this and get the output we really want, none of which are necessarily ideal.

The easy fix, if you can do it, is to simply change the wrap_content height/width to match_parent or a specific size (e.g. 100dp). Obviously, that will not always give you appropriate results.

A second option would be to change to some other container, like a LinearLayout. This will likely increase the depth of your view hierarchy, making your layout heavier and the layout pass take longer.

Finally, a simple extended version of RelativeLayout with an altered onMeasure method can give you the desired results at the expense of a second measure pass. By calling the super onMeasure method a second time, passing exact measurements, we can leverage the existing RelativeLayout code to do the hard work for us and give us the results we desire:

Overall, it was a good day. It was great to see so many folks from the Detroit area motivated to spend a beautiful Saturday learning more about mobile development. Lots of good presentations all around and an excellent keynote address by Mike Lee. Hope to see this event happen again next year.

Despite using Adapters to achieve complex results on a nearly daily basis, it didn’t really hit home with me just how complex and powerful they really are until I attended an “Intro to Adapters” presentation at GDG-A3 this past week. Joe Blough did a nice job showing how to get started with Adapters, you can get his slides and code if you wish to take a look. One particular point started a bit of discussion, and that was dealing with click listeners attached to views returned by the Adapter. Below are three approaches to solving this problem that came from that discussion. I’m sure there are others, and the solution that works for you will depend on your use case.

So, for simplicity’s sake, let’s consider a simple ListView with an Adapter that has one type of View. This view is pretty simple, containing just a TextView and a Button:

Now, in your Adapter, you will want to hook up a click listener to the button and perform an action when the user clicks. More than likely, what happens when the user clicks the button is going to depend upon which button is clicked. For example, perhaps the “Click Me” button has the always useful task of informing the user of the index of the row in which the clicked button resides.

Simple (Anti-)Pattern

A simple solution might result in a getView() method that looks something like this:

This code will certainly work. But consider for a moment the downsides. Every single time that getView() is called, a brand new OnClickListener object is created and the old one thrown away. This is potentially adding a lot of garbage to the heap. There has to be a better way.

(Before we move on, let’s consider a couple of things that are going right in this example: re-using the convertView and using the ViewHolder pattern. ListView is highly optimized for buttery-smooth scrolling. One of the optimizations is that it re-uses views so that they don’t have to be re-created or re-inflated hundreds or thousands of times, eating up extra memory and wasting CPU cycles. But this optimization relies on you properly making use of the convertView in your own implementation of getView(). The ViewHolder pattern is a further optimization that lets you avoid having to call the relatively expensive findViewById() method over and over again. But… you already knew all this anyway, right?)

A Better Way — A single OnClickListener

An easy change is to take advantage of the fact that the onClick() callback in the OnClickListener gets passed a reference to the View that was clicked, giving us an opportunity to pass some information into the listener. So rather than hundreds of unique and transient click listeners, we can instead have one single click listener that knows how to find the data it needs. In order to accomplish this task, we’ll do something similar to the ViewHolder pattern and take advantage of the tag on the View that is being clicked:

Notice how in this version, we have just a single click listener object (mMyButtonClickListener) that is connected to every instance of the button. We just tag a little bit of extra data on the button itself so that the click listener can figure out what to do.

An Alternate Better Way — A Click Listener in the ViewHolder

An alternate solution that Joe came up with and that I had not considered previously would be to put the click listener itself within the ViewHolder. This has the advantage that the click listener has easy access to the other View(s) within the row, making it very easy to change the state of those Views. For example, maybe you want to change the color of the text when the button is pressed:

So, there you have it, three ways to do the same thing, each with its own plusses and minuses. As I mentioned earlier, the solution that works best for you will be highly dependent upon what you are trying to accomplish. But hopefully now you’ve got a few more tools in your toolbox to help you the next time you run into this type of issue.

I spoke at the Michigan Google Developers DevFest 2013 today in downtown Detroit. It was a great day with lots of good presentations and was well attended. The presentation that I gave was an adapted version of the Animations in Android talk that I had given before, with a little less fluff and a little more detailed code.

Did you miss my talk? No worries, I’ll be presenting again at MobiDevDay on May 4th. That talk will be a more-focused version that digs deeper into Animators and leaves out pretty much everything else. See you there!

Well, not quite. If you keep reading that document, you will notice there are a couple of caveats. A very important one is to make sure that you are using the correct path to the user’s files. I have seen many examples around the web and elsewhere (including my own code) that uses hard-coded paths to access files in the private storage area. The code often looks something like this:

This code has worked great for a very long time, but if you want to support multiple users on a single device, this code will fail. Why? Well, the implementation of multiple user support separates the private files of each user from those of all other users on that device. This is a good thing as it means that users don’t have to worry about their files being exposed to those who shouldn’t see them. In order to do this, Android gives each user a unique space in the file system. So, while /data/data/com.example.mycoolapp might work for the administrator user on the device, another user attempting to access /data/data/com.example.mycoolapp will receive an error indicating that he or she does not have privileges to that location. That location is, in fact, owned by the administrative user and not the second user.

So, how to solve the problem? Google tells you right in the Android 4.2 APIs document, but ultimately the change is very simple. You just need to make sure that you have a Context reference available. To fix my example above, I would simply change that line as follows:

An easy, one-line code change that will save you the headaches and confusion that would have come when your users started reporting strange errors and the inability to use your app on their Android 4.2 tablets.

On January 31, 2013, I gave an “Animations in Android” talk at the Google Developer Group Ann Arbor Android (GDG-A3). I covered three ways to animate in Android: “Manual” where you do all the heavy lifting in the draw() method of your custom View or Drawable; using the Animation classes found in android.view.animation; and the Animator classes found in android.animation. The major focus was on the Animator classes, including the use of NineOldAndroids to support versions prior to Honeycomb.