Mark L. Murphy's Blog, page 66

May 6, 2013

Fun With Density Resource Set Qualifiers

The normal rule for resource set qualifiers, like -large or -land, is that only those that match the device’s current configuration will be used. One oft-mentioned exception to this rule are density resource set qualifiers, like -hdpi and -xxhdpi, where Android will choose from another “near” resource set if there is no exact match.



This latter capability was always described in the context of drawable resources, where Android has the ability to “convert” a resource from one set to another, such as downsampling an image. Therefore, I had assumed that this resource set trickery was a feature of drawable resources.



It’s not.



It’s a feature of the density resource set qualifiers.



(probably many of you already realized this, or guessed better than I did)



So, for example, if you have an app with res/values/strings.xml and res/values-xxhdpi/strings.xml, and you request a string resource on an -xhdpi device, my assumption was that you would get the one from res/values/strings.xml. After all, you do not match the density, and Android cannot really convert a string between densities, so you would get the default. In reality, at least on Android 4.2, you get the one from res/values-xxhdpi/strings.xml. Android applies the same rules for choosing a resource set based on density for strings as it does drawables.



Now, in truth, you should not run into this very often. Outside of maybe some game apps, having layouts or menus or preferences that vary by screen density is a strong code smell. But if you are tweaking dimension resources using density resource sets (for cases where the straight-up dp conversions do not quite give you what you want), or similar situations, just bear in mind that all resources, not just drawables, are affected by the way density resource set qualifiers are used.



Many thanks to Morrison Chang, whose answer and comments on StackOverflow triggered my research in this area.

 •  0 comments  •  flag
Share on Twitter
Published on May 06, 2013 16:40

May 3, 2013

The Busy Coder's Guide to Android Development Version 4.8 Released

Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.8, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!



This is a smaller update than normal, in terms of topics:





A new chapter was added on developing for the OUYA game console (many thanks to Al Sutton for his assistance!)





New large-screen samples, showing alternative ways of implementing the master-detail pattern, as riffs on the original EU4You sample





Expanded coverage of advanced ViewPager tricks, including using Jake Wharton’s ViewPagerIndicator and supporting “Plume-style” columns on large screens and pages in a ViewPager on smaller screens





Updated coverage of Presentation, including an even simpler sample that takes advantage of my CWAC-Presentation project





New material on creating custom views and containers, including a ReverseChronometer and explanations of the implementation (and usage) of the AspectLockedFrameLayout and mirroring classes from my CWAC-Layouts project





Updated everything to ActionBarSherlock 4.3.1





Began the slow process of replacing all fill_parent with match_parent, now that nobody should have a build target lower than API Level 8





Added part dividers to the PDF for the core chapters and each of the trails





The next update is tentatively planned for mid-June, though this may change depending upon what happens at Google I|O.

 •  0 comments  •  flag
Share on Twitter
Published on May 03, 2013 10:27

May 2, 2013

Interesting Tidbits from the Latest Device Dashboards

I don’t normally comment on the creeping changes in the Android ecosytem illustrated by the device dashboards, but I did want to point out a few things that I found interesting, or that you might be forgetting.



On the forgetting front, please remember that these dashboards only reflect devices accessing the Play Store. Devices that do not have the Play Store will not show up. Notably, this will include popular devices like the Kindle Fire series. If you are only distributing through the Play Store, the dashboards should be a solid picture. If you are distributing through other channels, though, take into account that those other channels may get you to devices that are not reflected in the dashboards.



This month probably is the last one for Android 1.x, as Android 1.6 is down to 0.1%, the threshold of irrelevance from the dashboard perspective. By this point in time, even if you are trying to maintain compatibility with Android 1.6, you should no longer be limiting your app to only 1.6 features. Set your build target and android:targetSdkVersion to be something newer, and using Java version guards (using Build.VERSION.SDK_INT) and Lint warnings to opt into newer capabilities. IOW, don’t let backwards compatibility be the albatross around your neck.



(not to mention that decaying albatrosses probably start to stink)



The Gingerbread decline continues apace. I expect that it will fall to 15% or lower by the end of 2013, despite the fact that Gingerbread devices are still made and sold. Frankly, I am expecting OEMs that are still shipping Gingerbread to cast a long look at Firefox OS as an alternative to Android, if they determine that Gingerbread has gotten stale, yet they feel that they cannot offer low-end ICS or higher devices.



What surprised me the most, though, was that xxhdpi devices are already up to 1.3%. These will be your SONY Xperia Z, Droid DNA, and similar 1080p handsets. Many apps will not need to worry immediately about this, as xhdpi assets will be upsampled and will work. The apps that really need to ponder this, plus the 1.0% tvdpi tablets (presumably all Nexus 7’s), will be those using <compatible-screens> to try to block their app from larger-screen devices. If you do not include support for all densities, you will be blocked from the missing densities as well. Alas, there is a bug in manifest parsing that may preclude you from using tvdpi and xxhdpi as symbols, forcing you to use numeric equivalents (e.g., 480 for xxhdpi) as an alternative. Or, get your app’s large-screen support up to snuff and get rid of <compatible-screens> entirely.

 •  0 comments  •  flag
Share on Twitter
Published on May 02, 2013 10:50

April 29, 2013

License Or It Doesn't Exist

The phrase “pics or it didn’t happen” is used often on teh Interwebs as an alternative to Wikipedia’s “citation needed”, pointing out a claim that needs evidence.



I’d like to propose another meme in the same vein: “license or it doesn’t exist”.



There are countless repositories on GitHub and elsewhere that lack a clear license for their contents. Putting a license on a body of open source code is usually a matter of a LICENSE file and appropriate file headers, so it’s not a ton of work. When I find self-promoted projects that lack such licenses, I try to convince their developers to properly license the work. Otherwise, technically, we have no rights to use their published code.



But this isn’t purely a question of code.



Spiderfly Studios has published a pair of icon fonts, designed for Android developers, to help avoid the proliferation of icons spread across N densities. I’m sure the fonts are lovely. However:





Font files aren’t human-readable (like, say, most Java source code), and so if there is licensing data baked into them, it is not obvious what that license is





The fonts are distributed in a ZIP archive that contains no obvious licensing information





The Web page containing the link to the ZIP archive has no specific license for this material





That Web page does have “Copyright © 2012 Spiderfly Studios. All Rights Reserved” in a footer





Fonts are messy from a licensing standpoint in the first place (e.g., what would it mean to have a font licensed under the GPL?). Not having clearly spelled-out rules for where and how we can use these fonts means that nobody should be using them — the “All Rights Reserved” statement has to be presumed to emcompass the ZIP archive.



This hardly seems like it is what Spiderfly Studios wants, given that they went to the trouble of creating the fonts, the ZIP archive, and the Web page pointing to them.



At least in the United States, all works created since 1976 are automatically copyrighted by their authors, with very few exceptions. Hence, saying “well, I did not include a copyright notice, so it’s free for all” is insufficient evidence for all jurisdictions.



If you want reuse, clearly license the terms for that reuse. Otherwise, the material may as well not exist.

 •  0 comments  •  flag
Share on Twitter
Published on April 29, 2013 15:36

April 25, 2013

CWAC EndlessAdapter Users: Upgrade, Please

If you have been using my EndlessAdapter, I strongly encourage you to upgrade to v1.2.1. There had been various reports of an exception in the field, but only today was I given a reproducible test case for it, and that bug should now be fixed.



If you are already on 1.x, there should be no code changes. Just update your Android library project, or grab a fresh JAR.



The error report was for:



java.lang.IllegalStateException: The content of the adapter has
changed but ListView did not receive a notification. Make sure the
content of your adapter is not modified from a background thread,
but only from the UI thread.

Lesson #1: Error messages may be misleading. In this case, the exception points out that this may be a threading problem. However, in my case, it turned out that this was not a threading problem, which led to a couple of lost hours of debugging.



Lesson #2: If you do something in an Adapter that changes what getCount() returns, you need to call notifyDataSetChanged() immediately.



In the case of EndlessAdapter, getCount() will return the getCount() from the Adapter that it wraps, or that value plus one. The latter case is when EndlessAdapter was told that there might be more data, and so the extra item is a “pending view” used to denote that there is data being loaded.



EndlessAdapter tracks whether or not there is data to be loaded in a keepOnAppending. I dutifully made this be an AtomicBoolean, so it is a thread-safe value. However, what I did not take into account was that toggling keepOnAppending between true and false would cause getCount() to return a different value. Eventually, notifyDataSetChanged() would be called, after which I was safe, but there was a window of time in between there where manipulating the list — such as tapping on a row — would trigger the IllegalStateException.



What that IllegalStateException really means is that the ListView thinks there should be N rows, but the Adapter is now claiming that there are M rows, with M != N. These values are synchronized via a call to notifyDataSetChanged(), but if getCount() changes before notifyDataSetChanged() is called, you have the window for potential problem.



My change is simply to call notifyDataSetChanged() any time I change the value of keepOnAppending, thereby triggering getCount() to change its return value.



In the field, you would see this problem when the EndlessAdapter was told that we have no more data (toggling keepOnAppending from true to false) but the user taps on a list item before we get a chance to append the last chunk of data, which would trigger a notifyDataSetChanged() call.



Many thanks to jbenf for reporting the way to reproduce the error, which allowed me to make this fix.

 •  0 comments  •  flag
Share on Twitter
Published on April 25, 2013 22:02

April 24, 2013

Disabled Notifications: Setting Survives Uninstall

Normally, when you uninstall and reinstall an app, everything reverts back to the state the app was in after the original installation.



This is not true with the “Show notifications” checkbox that controls whether Notification or Toast work on Android 4.1+ devices.



I stumbled upon this purely by accident. I was responding to a StackOverflow question about setAutoCancel(true) not working, and I wanted to just run my test app once to confirm it worked. I first fired it up on a 4.1 emulator, and it behaved fine, though I do not have a PDF viewer installed, and my sample works better when there is one (the notification, when tapped, opens a downloaded PDF file). So I ran it on my Galaxy Nexus… and it did not work.



The only obvious response I got was a cryptic warning in LogCat:



04-23 18:10:33.052: W/NotificationManager(20521): notify: id corrupted: sent 1337, got back 0

(where 1337 is the notification ID for this Notification)



After a quick search of teh Interwebs, I stumbled upon mention of somebody else having received this warning when they had “Show notifications” toggled off. I was fairly sure that wasn’t the case here, as my sample app had been uninstalled before I ran it, but I checked Settings anyway.



And “Show notifications” was unchecked.



(checking it, of course, fixed my problem)



Apparently, the last time I had run this sample on this device, I used it to test “Show notifications”, and I happened to leave that checkbox unchecked. However, since this setting was retained even after an uninstall, my newly-installed version was still blocking notifications.



Switching to view all entries in LogCat then showed the following error, on the next line:



04-23 18:10:33.052: E/NotificationService(930): Suppressing notification from package com.commonsware.android.downloader by user request.

However, since that comes from NotificationService, it will not show up in a LogCat filtering on application.



I am sure that many of you give advice to users with balky app installations to uninstall and reinstall. That can cure some ills, but blocked notifications is not one of them.

 •  0 comments  •  flag
Share on Twitter
Published on April 24, 2013 10:51

April 19, 2013

Android 4.2 + HDMI != Presentation Support

One would expect that part of the CDD or CTS for Android 4.2 upgrades would require that devices with support for external displays, such as via a micro-HDMI port, support the Presentation class for displaying content on those external displays.



This does not appear to be the case.



The ASUS Transformer Infinity was upgraded to Android 4.2.1. While DisplayManager reports the HDMI display (when one is connected), there are two flaws that I see:





It reports invalid configuration data (e.g., a 720p projector is reported as being 1080p)





Attempts to use that Display with a Presentation object fail, in that nothing happens – normal mirroring continues





There are also reports that the Galaxy Nexus does not support Presentation when used with an MHL cable, though I have not attempted to reproduce that.



Hence, code that works fine with the Nexus 10 (micro-HDMI) and Nexus 4 (Miracast) does not work on the ASUS Transformer Infinity.



Ideally, the CDD or CTS would check for this, but apparently that is not covered, which is why I am calling for better support here.



Ideally, I would have discussed this with ASUS, but ASUS seems to lack any developer support options. Even Twitter just routed me to end-user support. If anyone at ASUS happens to read this and wishes to discuss this with me, please feel free to reach out.

 •  0 comments  •  flag
Share on Twitter
Published on April 19, 2013 09:04

April 17, 2013

Don't Have an Accidental API

Let’s say that you have a popular application. Furthermore, lets say that you have one or more BroadcastReceiver components, registered in the manifest, with custom actions in <intent-filter> elements.



By default, you have created an API.



In fact, you have created what amounts to two APIs:





Other apps can send broadcasts to your receiver.





Other apps can monitor your broadcasts, by having their own receiver tied to that same Intent action (which they mined out of your manifest via decompiling)





The former can be fixed by not exporting the component (android:exported="false"), though in this case you really should not have the <intent-filter> in the first place, most likely. Just use an explicit Intent to communicate with this receiver, as <receiver> elements without <intent-filter> elements are automatically not exported.



Both can be secured by use of permissions (particularly custom signature permissions), or by changing away from public broadcasts in general (e.g., switch to LocalBroadcastManager).



If that sounds like work, well, it is.



If you wonder why you need to go through that work, bear in mind that others out there are aware of these accidental APIs and will try to exploit them.



Any exported component is an API, whether intentional or accidental. So, an activity with an <intent-filter> can be started at any time, for any reason, by anyone. Likewise a service or a ContentProvider.



Intentional APIs, with documentation and support and the like, are wonderful. Accidental APIs represent possible security issues at worst, and possible dependency issues (“hey, I was using the XYZ that you had exported, and now my ‘add-on’ app is broken!”) at best.



Create an intentional API. Don’t have an accidental API.

 •  0 comments  •  flag
Share on Twitter
Published on April 17, 2013 07:44

April 16, 2013

Developer Support Teams: Monitor a StackOverflow Tag

If you offer support for Android developers related to your firm, you should have StackOverflow as part of your support platform.



Here, I am thinking of:




Device manufacturers

Service providers (e.g., mobile-focused “backend-as-a-service”)

Anyone with an Android-specific API


Why should you have StackOverflow as part of your support platform? Because, like it or not, your developers are going to ask questions there.



Just within the past few hours, we have questions about:




Spotify

Kii Cloud

Dropbox

Samsung Galaxy Note 10

Pushwoosh


And I am not even counting questions about Android open source packages (e.g., Robotium), development tools (e.g., Eclipse, IDEA), etc.



Some firms already provide support this way:




Amazon monitors the amazon, amazon-appstore, and kindle-fire tags

HTC monitors the htc and htcdev tags


Developers will ask questions on StackOverflow about your products and services for any number of reasons:




Maybe they are not aware that you have other developer support options

Maybe they have had… sub-par results from your other developer support options

Maybe they just naturally ask questions on StackOverflow by reflex


The good news is that StackOverflow’s tag system makes it very easy for you to find out about many of these questions relevant for your team:





Set up a free account on StackOverflow if you do not already have one.





Find any existing tags that refer to your firm’s products and services. If there are no such tags, or you would like to have other/different tags, re-tag a question to define the new tag. If you do not have enough “reputation” for that (such as you have a brand-new account), drop me a line and I can try to assist.





Set up to be notified about questions newly tagged with your tag, by either subscribing to the RSS feed (URL syntax is http://stackoverflow.com/feeds/tag?ta...) or setting up a filter to email you questions, or via the Question Monitor for StackExchange Chrome extension, or any number of other tools.





Let developers know that you are actively providing support via your tag(s), through your normal communication channels (e.g., social media) and by adding an entry in the android tag wiki (if you lack the ability to edit this page to link to your tags, let me know and I may be able to help). In particular, if StackOverflow “regulars” know about your tags, they will be able to re-tag questions that should have one of your tags, but do not.





When you supply answers to questions, feel free to include links to relevant resources from your normal developer support site, such as forum pages discussing the same issues. This way, not only do you help developers now, but you help inform developers about the other support resources that you offer.





This takes but minutes to set up. Beyond that, it simply adds more questions to be answered on top of the ones coming to you via other means — how much additional work this is, of course, will vary. But, more importantly, you may be able to assist developers that would otherwise “fall through the cracks” of your developer outreach programs, increasing the odds that they will be satisfied with your developer support.



…as opposed to a device manufacturer, who (temporarily) shall remain nameless, that really needs a developer support program but lacks it. I will gripe about that particular firm later in the week.

 •  0 comments  •  flag
Share on Twitter
Published on April 16, 2013 06:37

April 11, 2013

Jeff Gilfelt's Android SDK Samples Search

Jeff Gilfelt has a Chrome extension that augments the SDK JavaDocs with “(sample code)” links, much like how Roman Nurik’s Chrome extension adds “(view source)” links. In the case of Jeff’s extension, those “(sample code)” links then execute a search on GitHub for references to that specific class. The search covers Android’s platform_development repo (as mirrored on GitHub) plus my own cw-omnibus repo for The Busy Coder’s Guide to Android Development. Hence, you can find sample code from my book directly from your Android SDK JavaDocs, effectively cross-indexed by the Java classes used by those samples.



Many thanks to Jeff for publishing this extension, and for including my samples in it!

 •  0 comments  •  flag
Share on Twitter
Published on April 11, 2013 06:42