Mark L. Murphy's Blog, page 69
January 22, 2013
Letting the Device Sleep, Intelligently
Android makes it easy for activities to keep the screen on while the activity is in the foreground, by means of android:keepScreenOn and setKeepScreenOn().
However, these are very blunt instruments, and too many developers simply ask to keep the screen on constantly, even when that is not needed and cause cause excessive battery drain.
For example, let’s talk about Eufloria HD.
Eufloria is a delightful game. It arranges to keep the screen on during game play. That’s probably a good idea, as screen timeouts can be fairly short (e.g., 15 seconds), and while usually the user will be tapping or swiping something within that period of time, they might not always do so while still playing the game.
However, Eufloria always keeps the screen on. So, if you press the in-game pause button, they keep the screen on while the game is paused. This might lead the user to press pause, put down their tablet (expecting it to fall asleep in a normal period of time), and then have the tablet keep going and going and going… until the battery runs dead.
Whether you use setKeepScreenOn() or directly use a WakeLock (as the permissionss suggest that Eufloria does), it is useful to think of three tiers of user interaction.
The first tier is when your app is doing its “one big thing”: playing the game, playing the video, displaying the digital book, etc. If you expect that there will be periods of time when the user is actively engaged with your app, but is not interacting with the screen, keep the screen on.
The second tier is when your app is delivering something to the user that probably would get used without interaction in the short term, but not indefinitely. For example, Eufloria might reasonably expect that 15 seconds could be too short to have the screen time out, but if the user has not done anything in 5-10 minutes, most likely they are not in front of the game. Similarly, a digital book reader should not try to keep the screen on for an hour without user interaction.
:: note to self: fix this in my digital book reader… ::
The third tier is when your app is doing anything other than the main content, where normal device behavior should resume. A video player might keep the screen on while the video is playing, but if the video ends, normal behavior should resume. After all, if the person who had been watching the video fell asleep, they will not be in position to press a power button.
The first and third tiers are fairly easy from a programming standpoint. Just acquire() and release() the WakeLock, or toggle setKeepScreenOn() between true and false.
The second tier – where you are willing to have a screen timeout, just not too quickly – requires you to add a bit more smarts to your app. A simple, low-overhead way of addressing this is to have a postDelayed() loop, to get a Runnable control every 5-10 seconds. Each time the user interacts with your app, update a lastInteraction timestamp. The Runnable compares lastInteraction with the current time, and if it exceeds some threshold, release the WakeLock or call setKeepScreenOn(false). When the user interacts again, though, you will need to re-acquire the WakeLock or call setKeepScreenOn(true). Basically, you have your own inactivity timing mechanism to control when you are inhibiting normal inactivity behavior or not.
Now, in Eufloria’s defense, they might be using a third-party game framework that does not provide this degree of control at the present time. That, though, means that the game framework could use a bit of sophistication in this area.
By having a more intelligent use of WakeLock and setKeepScreenOn(), you can deliver value to the user while not accidentally causing excessive battery drain. Users do not always remember to press the power button, so you need to make sure that just because the user made a mistake, that you do not make it worse.
January 15, 2013
Toasts: Should Not Be Important, May Be Blocked
I do not claim to be a UI/UX expert, nor do I play one on (Google) TV.
However, it stands to reason that things that are important should be visible for longer than a few seconds, wherever possible.
In other words, purely transient effects, such as a Toast or the “ticker text” of a Notification, should not be the sole source of important information. It is far too easy for a user to miss these, partly due to size (both tend to be small), but mostly because they are short-lived. All the user needs to do is be distracted for a few moments at the wrong time, and they will miss the important information.
If you want to use a Toast or Notification ticker in addition to something else, that is fine. Or, perhaps the “something else” is sufficient for your needs, such as displaying the information in a banner-style area within your activity. It just has to stick around if it is truly important.
This is compounded by the fact that, on Android 4.1+, if the user disables notifications, this also disables the display of a Toast. Even the foreground activity cannot display a Toast if notifications are blocked for the app.
This behavior is flagged as a defect, and so it is possible that Toasts will return despite notifications being blocked, or be allowed in certain circumstances (e.g., processes with foreground priority).
But since users can block a Notification (and, by current extension, a Toast), and since the ticker text and the Toast are transient, do not use them as the sole means of delivering important information. Your users should be worth a better UI.
(also, thanks to Harri Smått for pointing out this issue!)
January 3, 2013
The Busy Coder's Guide to Android Development Version 4.5 Released
Happy New Year!
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.5, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!
This release:
Adds ~50 pages of coverage of the new “Maps V2” mapping option for Android, with material roughly matching the coverage of the original “Maps V1”
Adds a standalone tutorial for creating an app that uses SQLCipher for Android, based on a workshop delivered at AnDevCon IV in December
Adds material to the app widgets chapter on resizeable app widgets and lockscreen app widgets
Adds material to the device admin chapter on controlling lockscreen behavior, including a sample app that allows you to disable the lockscreen support for app widgets and the camera
Adds coverage of ShareActionProvider
Removes the chapter on the WIMM One, as WIMM Labs is no more
Fixes lots of errata and adds other minor improvements
There are other changemarks that you will see in this edition, related to other sorts of changes:
Fixed most, if not all, of the broken curly quotes, as the publishing process’ “smart quotes” turned out to be not quite as smart as anticipated, so many paragraphs where the quotes changed will have changemarks for that reason
All samples using ActionBarSherlock were updated to have android:targetSdkVersion of 14 or higher, to eliminate duplicate overflow menu affordances on some devices, so a few manifest code listings will have changemarks for that reason
In terms of the book formats, the EPUB, MOBI/Kindle, and APK files have larger images. The APK file also supports a long-tap on an image, which will then open up in a separate activity that supports zoom and pan operations, for you to be able to examine the image more closely. These changes did increase the file sizes by a fair bit, though.
Version 4.6 is planned for mid-February.
December 27, 2012
Disable Unusable Activities
If you examine the manifest file for the AOSP Email app, you will notice something interesting: some of the activities ship disabled.
For example, the MessageCompose activity — which is responsible for handling ACTION_SEND and ACTION_SENDTO requests — has android:enabled="false" on its <activity> element.
The reason is that this activity is useless until the user has gone in and set up one or more email accounts. The Email app, therefore, leaves this activity disabled by default, enabling it only after the user has set up an account.
This way, Email does not show up unnecessarily in choosers for ACTION_SEND and ACTION_SENDTO. While the Email application has the code to handle those requests, it does not always have the configuration to handle those requests. By disabling the component, the Email app ensures that the user will only be able to choose the Email app when the Email app can actually fulfill the request.
If your application supports common implicit Intent actions like ACTION_VIEW, ACTION_SEND, and so on, you should consider following in Email’s footsteps. If your app can always handle those actions, having a regular activity is fine. If, however, your app requires user setup before it can handle those actions, disable the activity at the outset, enabling it via PackageManager and setComponentEnabledSetting() when the activity becomes applicable.
December 19, 2012
Warning: Do Not Use android.hardware.camera.any Yet
It’s amazing the things that you stumble upon when reading StackOverflow.
A gentleman posted a popular sort of question, inquiring about why his app was listed by the Play Store as supported by “only 3 (out of 2263)”. As is typical with this sort of question, he posted his manifest, in hopes that we could determine what rogue element was causing his grief.
One element popped out at me: <uses-feature android:name="android.hardware.camera.any" />
My initial reaction was that this was an invented feature. It turns out that android.hardware.camera.any is real and was added in API Level 17 (Android 4.2), to address the issues caused by the Nexus 7 and potential other future devices with a front-facing camera and no rear-facing camera.
However, this issue indicates that the Play Store is not up to speed just yet:
However, the Play Store needs new logic to provide backwards compatibility for devices with older OS releases, which is not yet ready.
So until that support is in place, don’t use “android.hardware.camera.any”, as any apps requiring it will not yet be installable on devices running pre-4.2 versions.
That certainly would fit the symptoms, as Android 4.2 is only officially available for a few models right now.
Alas, there is no warning in the actual documentation about this limitation.
Hence, be very careful when using android.hardware.camera.any. Either avoid it altogether or make it not required (android:required="false"). If you do elect to use it, double-check your entry in the Play Store to make sure that your app is showing up for the right number of devices, until we collectively feel comfortable that the Play Store has indeed taken this new feature into account.
December 17, 2012
Maps V1 Keys: Going, Going, ...
According to the Android Maps v1 documentation:
Note: Version 1 of the Google Maps Android API as been officially deprecated as of December 3rd, 2012. This means that from March 3rd, 2013 you will no longer be able to request an API key for this version. No new features will be added to Google Maps Android API v1. However, apps using v1 will continue to work on devices.
This has several ramifications if you are using the v1 Maps SDK.
First, if your debug key becomes invalid after March 3rd — such as because it expired a year after creation — you will not be able to get a new Maps v1 API key for your new debug key. Recommendation: delete your existing debug.keystore in late February, let the SDK tools regenerate it, then grab a new v1 Maps API key for the new keystore. Then, make a dozen or so backups of the keystore and the Maps v1 API key.
You also should be backing up your production keystore and its associated Maps v1 API key, for much the same reason. Hopefully, you have been doing this already. If not, please stop reading this blog post and go set up a decent backup regimen for your development machine.
If you are a consultant, and you have been using unique keystores (debug or production) for each client, you better stock up on keystores and associated Maps v1 API keys. In theory, assigned API keys should work indefinitely (“However, apps using v1 will continue to work on devices”), so just set up a bunch, enough to tide you over until you will be using Maps v2 for all your new clients.
Similarly, if you take the approach of having unique production keystores per app on the Play Store (e.g., to make it a bit eaiser to sell full rights to the app to some possible buyer), either plan on moving to Maps v2 ASAP or similarly stock up on some keystores and Maps v1 API keys.
Moving to Maps v2, for many apps, should be fairly straightforward. And, as Jeff Gilfelt pointed out, Maps v2 has a comparatively nice API. However, there will be some developers who run into problems with Maps v2, either due to bugs, device support, or API limitations (e.g., no ability to draw() map markers).
That being said, Google really should issue Maps v1 API keys for 12 months, not 3 months, from when Maps v1 was deprecated. Expecting everyone to be able to switch in 3 months — some of which is lost due to holidays — is unreasonable. The V2 API and feature set is sufficient of a carrot to drive developers to want to move; beating developers with the deprecation stick might drive developers to abandon Google’s maps entirely in favor of other solutions with less anal policies. As it stands, I fully expect much wailing and gnashing of teeth starting on March 4th, as developers missed all of these unannounced announcements and get screwed by the lack of available API keys.
December 12, 2012
Think About Font Scale
For years, sp was the guest nobody wanted at the dimension unit party. While we were told that sp units would scale with the font scale, users and developers had no good means of affecting the font scale. As a result, many developers skipped sp in favor of other units for text sizes, simply because they understood the other units better.
However, for the past year, we have had users capable of changing font scale. Android 4.0 added a “Font size” option to the Settings app (in stock Android, you will see this under the “Display” header), allowing users to switch between a few pre-defined scales (small, normal, large, huge).
For text sizes specified in sp, the actual size scales based upon this font size selection. So while 1sp == 1dp for “normal” scale fonts, 1sp > 1dp for “large” and “huge” scale fonts, and 1sp < 1dp for “small” scale fonts.
By contrast, text sizes in other units of measure, like dp, are unaffected by this.
Of course, the knee-jerk reaction of many developers will be to rip out any sp units they might have and replace them with dp, since they cannot control the absolute size of sp units. That would be a mistake. Just as users of desktop Web browsers may adjust font scale to make sites more readable, users of mobile devices might adjust the system font scale to make apps more readable. Freezing your text sizes using dp simply means that your app will be out of step with other apps, notably the system-supplied apps. And, since the user is asking for the different-sized fonts, the user may be disappointed if your app ignores the request.
Testing font scale is “another one of those things” to add to your testing list, along with testing on different densities, testing different langauges (to ensure strings do not run too long), and so on.
December 11, 2012
Allow Overrides of Your Overrides
Alan Zeichick is the conference chair for the AnDevCon series. He uses an Android phone. And he has a complaint.
One of his apps (I didn’t ask which) is some sort of replacement SMS client. It will chirp whenever an SMS arrives, and continue chirping every so often until he reads the message. That part is fine.
However, it will chirp even if he has the phone set to vibrate or mute.
In other words, from his perspective, the app is overriding the device-level volume setting.
This too is fine… as an option. If the user wants this sort of chirping despite having the phone on vibtate or mute, that’s fine.
However, the user should be given a choice about this behavior. As it stands, Alan is frustrated with the app, which he otherwise likes a lot. Frustrated users are unlikely to give you five-star ratings on the Play Store, or recommend your app to others in private conversation, or extol the virtues of your app in front of an audience of Android developers at conferences.
You can see the same behavior with the stock alarm clock app: the alarm rings audibly despite the vibrate/mute setting of the device. However, at least with the alarm clock, you are opting into individual alarms. Users of SMS clients have little control over when SMSes arrive.
This may be purely accidental. Android has the notion of separate audio streams with separate volumes. It may be that Alan’s device’s interpretation of mute/vibrate does not affect the stream that the app is using. That does not change the user perception that the app is at fault, though, as users will only perceive this pain from the app, even if the device or Android is really the culprit. The app developers may need to experiment and give the user the option of which volume to use (e.g., STREAM_RING instead of STREAM_ALARM), so device-level muting works as expected.
Alan’s reaction was (and I’m paraphrasing here), “with an iPhone, when I say ‘mute’, everything mutes”. This is illustrative of the difference between iOS and Android. iOS, more than Android, enforces behavior that Apple feels is appropriate. Android gives developers the option of doing things differently, because there are cases where users would like it if we would “think different”. However, as a result, Android gives developers enough rope to hang themselves and their users… even if Android or the device manufacturers sometimes play the role of the hangman.
If you are doing something that would behave counter to the user’s perception of the current default device configuration — from chiming while muted to running background services — the user needs a choice. The user needs to be able to override the override.
November 26, 2012
The Busy Coder's Guide to Android Development Version 4.4 Released
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.4, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!
This release:
Moves all the device and accessory material (Google TV, Kindle, NOOK, etc.) into a separate section at the end of the book. Just as the Widget Catalog chapters will enumerate various widgets not covered significantly elsewhere in the book, the Device/Accessory Catalog has all of the device-specific chapters.
Adds a new chapter on focus management and accessibility
Expands the SQLCipher for Android and anti-patterns chapters
Adds a SeekBar chapter to the Widget Catalog
Updates the tutorials to work on the R21 edition of the Android Developer Tools
Adds some miscellaneous Android 4.2-specific material, including notes about multiple user accounts, new default ContentProvider export behavior, and the revised status/system bar plan for tablets
Adds a sample of using nested fragments, in the form of having a ViewPager be hosted in a fragment and use fragments for pages
A number of errata fixes or other minor improvements (e.g., better configuration change support for the ThreePaneDemo)
All told, this adds not quite another 50 pages of material.
Version 4.5 will be released in early January 2013. Planned enhancements include: lockscreen widgets, FragmentTabHost, ShareActionProvider, and more.
Note that the price for a Warescription will increase from $40 to $45 on January 1, 2013. This is the first price increase in three years. Since renewing your Warescription simply adds time onto any existing Warescription, if you know you are planning on renewing, and would like to save $5, do so before the end of the year. After this, the next price increase is not planned until 2016.
November 21, 2012
Miscellaneous Android 4.2 Regressions
Lots of people have already chimed in on StackOverflow about Settings.Global, notably the fact that AIRPLANE_MODE is no longer available to be modified in Settings.System. Now, only signature|system apps will be able to toggle on and off airplane mode, which will wipe out many an app widget. To be honest, this did not surprise me, as I never fully understood why it was left in Settings.System in the first place when Settings.Secure was carved out in 1.5.
That being said, I don’t think that it’s unreasonable for there to be a secured means from within the Android SDK to modify these sorts of settings. If a regular permission is deemed insufficient, then make them toggle-able via the device admin API, the way we can disable the camera.
Along a similar line, there has been another regression in the world of APN settings. Android 4.0 locked down the WRITE_APN_SETTINGS permission, making it signature|system, so ordinary SDK apps could no longer modify APN values. Android 4.2 has taken the next step and is using WRITE_APN_SETTINGS to secure read access to APNs. The argument given in the commit is:
Since the DB may contain corp passwords, we should secure it. Using the same permission as writing to the DB as the read is potentially as damaging as a write.
Once again, while I certainly can see the argument for blocking unfettered access to APN data, these are the sorts of changes that really should be available to device admin apps. While the vast majority of Android users do not need apps messing with APNs, and carriers should be working with their device manufacturers (who can offer signature|system apps), enterprises are left in the cold.
Another issue with all these changes is their unannounced nature. Sometimes, as is the case with Settings.Global, we are informed about them. Sometimes, as in both of the WRITE_APN_SETTINGS changes, we find out about them because apps crash in testing, and developers wonder why. You would think that by a version 4.2, a process could be in place where app-affecting changes like WRITE_APN_SETTINGS changes would be collected and published as part of the release notes.


