Getting Ready for ChromeOS
A bit over two weeks ago, I delivered
an “Android on the Desktop” presentation
to the NYC Android Developer Meetup.
A couple of weeks before that, I blogged about
the signs that Android apps were coming to Chrome OS.
A year prior to that, I blogged about
the new option for distributing Android apps to Chrome OS.
last week’s announcement on this came out,
while I don’t mean to say “I told you so”… but I told you so.
Admittedly, it is not happening quite as I expected. Reading between the
lines of the announcements, the Ars Technica article,
and the VentureBeat article,
it appears that Google jettisoned the ARC approach. Instead, it seems
as though our apps are going to be running in a beefed-up edition of
the Android SDK emulator, one with some platform-specific hooks (e.g.,
integrated window management with Chrome OS-native windows). And they
feel confident enough about this approach that your app will, indeed,
wind up shipping to Chrome OS unless you take steps to block it.
So, now what? In particular, if you want to support Chrome OS, what
should you do?
First, try to get your hands on compatible hardware. At least here
in the US, two of the three near-term test devices
are relatively inexpensive, in the $200-300 range. These devices should
get the Android app support in June. Note that all three have touchscreens.
You will then want to review your
<uses-feature> elements, along with
<uses-feature> elements coming from your
Ideally, you have few of these, and those that you have are set for
android:required="false". The more that you require, the less likely
it is that your app will work on Chrome OS. In particular, pay attention
to hardware features that may not be on Chrome OS devices, remembering
that not all Chrome OS devices are notebook equivalents. Some are desktop
replacements (e.g., Chromebox). So, while many Chrome OS devices will
have a camera, some will not. Also, since your app cannot supply Chrome OS
with an input method editor, app widgets, live wallpaper, or a
replacement home screen, their corresponding
should be set to not be required.
Next, confirm whether your app can work without a touchscreen. For example,
you might hook up a Bluetooth keyboard and mouse to a test tablet, then
try using your app. If you determine that your app can be used successfully without
a touchscreen, add the following to your manifest, as a child element
of the root
<uses-feature android:name="android.hardware.touchscreen" required="false" />
This will allow your app to be used on Chrome OS devices that lack a
touchscreen, when such devices start getting Android apps later this year.
Once your test Chromebook gets Android app capability, look for instructions
for how to test your app on it. Ideally, it will work with our development
tools the same way that devices and emulators do, but that is not assured.
Note that this is supposed to arrive in M53 on the developer channel
to these devices, so you may need to make some changes to your test Chromebook
to pull in from the dev channel instead of the standard public channel.
If you decide that you do not want to deal with Chrome OS at all, you
will need to decide how you want to block such distribution. Presumably,
you will be able to do this via the Play Developer Console, but it might
not be that easy, if you have to block distribution on a device-by-device
basis. You might cheat and add in a
<uses-feature> element that should
be supported by mobile devices but would not be supported by Chrome OS
<uses-feature android:name="android.software.app_widget" />),
even if your app does not actually implement such a feature, just to tide
you over until you add Chrome OS support later.
And you will want to add Chrome OS support later. Chrome OS is interesting,
but at its estimated install base, it is roughly equivalent to Froyo
(0.1% of Android devices). On its own, therefore, it is not worth a lot
of effort. However, it seems rather unlikely that Google will stop here.
In the next couple of years, I expect Google to replicate this same approach
on Windows, Mac, and Linux. That audience is substantially larger and
therefore more compelling. Use Chrome OS as an extended test run for
eventually supporting classic desktop operating systems in the future.
You can read more about all of this:
I will be updating my book to reflect these changes as they
roll out over the next several months, so subscribers can keep up with
how to support Chrome OS.
—May 25, 2016
Random Musings on the N Developer Preview 3
Each time Google releases a new developer preview, I try to
talk about the changes that Google isn’t talking about.
N Developer Preview 3 (NDP3) represents another incremental change over N Developer
Previews 1 and 2. Google did not bother with an announcement blog
post, AFAICT, though there are
some release notes.
This post outlines some things that have caught my eye in NDP3.
Regressions and Changes… Compared to Production Android
postDelayed() methods, as does
On the whole, I have been steering developers to use the ones on
as you pretty much always have a widget available, so there is little
sense in creating a
Handler instance just for
postDelayed(). However, NDP3 makes a slight change here:
If an app posts
Runnable tasks to a
View, and the
View is not attached to a window, the system queues the
Runnable task with the
Runnable task does not execute until the
View is attached to a window.
For a lot of uses of
post(), this will not be a problem.
might get more interesting, particularly if you are using a “
postDelayed() invoking a
Runnable that calls
postDelayed() to schedule
itself again). That loop will only work so long as the
View you are using
is attached to a
Window. If you are concerned about this, beyond
testing your existing implementation, you might consider switching to
Handler instance for
- The week number feature on
CalendarView is now deprecated
- The embedded
DatePicker is now deprecated
and is “not supported by Material-style
Regresssions and Changes… Compared to NDP2
ShortcutsManager added in NDP2 still exists in NDP3, but the
release notes indicate that it will be retired in a future developer
preview. I would not recommend investing further time in it.
Other Stuff in NDP3
There are other curiosities tucked into the API differences report,
above and beyond the items called out in the release notes:
There is now an
Intent action, to
“launch an activity showing the app information”. This appears to be
tied to the Play Store and other app installation channels, suggesting
that you can use this to display information about apps that are not yet
If you create an
Intent directly (rather than via
createChooser() helper method), you can add
to list particular components that should be excluded from the list
of choices. This is very handy for cases where you have your own activity
that handles some implicit
Intent, but you specifically want to open
Uri in any other activity handling that type of content. For example,
perhaps you are having difficulty rendering the content, and you want to
allow the user to try viewing that content in a third-party app.
There is a new
Notification, which is a
“helper class for generating large-format notifications that include multiple back-and-forth messages of varying types between any number of people”.
I have not tried this yet to determine whether it is something that
might be specific to certain form factors (e.g., Wear) or whether it is
available across the board.
Some options that we have on
JobScheduler, such as “only while charging”,
and “only while idle”, are now available on
DownloadManager via methods
stopForeground() is being migrated from taking a
boolean (whether or not
to remove the
Notification) to an
JobScheduler, to allow us to
ContentProvider for changes without having an always-running
service. NDP3 adds
setTriggerContentUpdateDelay(), to postpone running
the job until the content has stabilized, so jobs do not run multiple
times for a series of minor changes to the content.
sets a cap on that delay. Between those two, you can have a modicum of
control over how frequently your content-related jobs are invoked.
android.view.PixelCopy method allows you to asynchronously(?)
copy the contents of a
Surface to a
Bitmap, with a callback when that
work is completed.
Bugs in NDP3
Some of the bugs that I filed against NDP1 and NDP2 were fixed,
particularly ones related to quick-settings tiles.
However, some bugs that I filed are still outstanding, such as:
I also have not yet confirmed whether the
that I filed has been fixed… as I cannot get
work at all anymore. I am still investigating this.
I am sure that many more things changed in NDP3 than I caught in the
past couple of hours. And most of what
I pointed out in NDP1
still hold true.
—May 24, 2016
SAFFAQ: The Storage Access Framework FAQ
Given the impending demise of
you have two primary means of getting access to documents that your
app wishes to use:
Have other apps push
Uri values to you, via activities
in your app with
<intent-filter> elements for common operations,
Uri values into your app – akin to using “file open”
or “file save” dialogs – via the Storage Access Framework
With that in mind, here are some fictionally-asked questions about
using the Storage Access Framework.
How Do I Ask the User for a Document?
On API Level 19+ devices, create an
with a MIME type indicating what sort of document you want,
CATEGORY_OPENABLE as a category. Start
startActivityForResult(). If you get
Uri in the delivered
Intent will point
to the document in question. Then, you can use
DocumentFile, and kin to work with the document,
as I outlined in an earlier blog post.
Why Do People Seem All Angst-y About This, Then?
Well, there are issues.
First is the API Level 19+ requirement. Some developers are already
up to a
minSdkVersion of 19 or higher, but plenty of others are not.
The fallback mechanism is
ACTION_GET_CONTENT and a MIME type.
Google, in its infinite wisdom, thinks that the UI presented by the
Storage Access Framework should be optimized for things like Google
Drive. As a result, the user has to know to do a bunch of clicks to
actually get to where their files are (what we call external storage,
what the user calls internal storage).
If you need long-term access to the document, beyond just your current
process, you need to use
takePersistableUriPermission() to try to
get that long-term access. However, you may not get it, because it
may not be offered. At the same time, though, you may lose access to the
document for other reasons (e.g., the user deleted it), so this should
not be a huge impediment.
What If I Want a Wide Range of Possible MIME Types?
EXTRA_MIME_TYPES on the
Intent, as a
String of concrete
or wildcard MIME types.
What If I Want a Bunch of Documents at Once?
EXTRA_ALLOW_MULTIPLE to the
with a value of
true. Now, instead of getting a single
you may get back several
Uri values. You access these through the
ClipData object you get by calling
getClipData() on the
getItemCount() on the
tells you how many documents the user opened, and
(for a value of
i) gives you the
Uri for that position
What If I Want to Create a Document?
ACTION_CREATE_DOCUMENT instead of
ACTION_OPEN_DOCUMENT, and make
sure that you use a concrete MIME type (e.g.,
Otherwise, it works the same as with
ACTION_OPEN_DOCUMENT: you get a
Uri back representing the newly-created document, and you can use
ContentResolver to get an
OutputStream onto which you can write your
What If I Want to Create a Bunch of Documents?
If the “bunch of documents” can (or should) reside in one directory,
ACTION_OPEN_DOCUMENT_TREE on API Level 21+ devices. This amounts
to a “directory picker”. You get a
Uri back representing a tree
of documents, and you can use methods on
DocumentFile to create
new documents in that directory.
What If I Want to Create a Bunch of Documents on Older Devices?
ACTION_CREATE_DOCUMENT several times, on API Level 19+
What If I Want to Create a Bunch of Documents on Seriously Old Devices?
There is no great way that I can think of to accomplish this through
Intents and such.
How Do I Delete a Document?
delete() on a
DocumentFile for the document. That, in turn,
calls the static
deleteDocument() method on
How Do I Delete a Directory?
delete() on the
DocumentFile. I do not know if this
is supported, but it is worth a shot.
Why Do We Have to BLEEEEEEEEEEEEEEEP With All This?
Partly, the reason is security. Privacy advocates have been complaining
for years about how apps can run around and mess with everything
on external storage, causing various issues. For example, apps with
read access to external storage can examine photos for geotags; coupled
with the photo’s timestamp, you now know where the user was at the time
that the photo was taken. Hence, over the years, Google has been
progressively tightening down Android with respect to file access.
Partly, the reason is that the documents the user wants may not necessarily
be on external storage, and so developers who fixate on local files will
harm the broader Android ecosystem.
For example, if Android on Chrome OS continues to expand,
external storage for ARC apps is not the Chromebook’s hard drive.
Rather, it is more like the external storage for an emulator, accessible
only by apps and through development tools. Worse, the current ARC
environment has each app walled off in its own AVD-style container, so
one app cannot access another app’s external storage. Presumably, the
vision is for apps to be using the Storage Access Framework, where
the ARC environment can bridge between the Android app and the Chrome
OS file areas that Chromebook users are used to working with.
Similarly, if we start seeing Android more in desktop-style settings,
desktops in an organization often rely upon a file server, such as a
Windows SMB server. Android can offer access to that via the Storage
Access Framework, as an SMB server is not significantly different than
is Google Drive from the standpoint of Android. However, once again, this
requires developers to start thinking in terms of documents identified
Uri values, more so than thinking purely about local
—May 03, 2016
The Busy Coder's Guide to Android Development Version 7.3 Released
Subscribers now have
access to the latest release of The Busy Coder’s Guide to Android Development,
known as Version 7.3, in all formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!
In terms of content, this update:
Updated the book for Android Studio 2.1, including a much larger
chapter on using the emulator, covering many of the new emulator
Expanded the appendix on the N Developer Preview, updating
it to the N Developer Preview 2 and adding material on
and network security configuration
Expanded the coverage of
consuming documents through the Storage Access Framework,
ACTION_OPEN_DOCUMENT_TREE and Android N’s
Expanded coverage of Android N multi-window, both in
the Android N appendix and in
the Storage Access Framework chapter, the latter
adding a multi-tab/multi-window text editor example
Merged the JUnit and JUnit4 chapters into one, with related fixes to the
other major chapters on testing and updates based on Android Studio 2.1
Updated coverage of
Notification to improve the material on progress
notifications and custom views, coverage of Android N’s
quick-reply working on the lockscreen, and the use of
Added an improved
ACTION_IMAGE_CAPTURE sample to
the chapter on working with the camera, using a
File objects, to deal with Android N
Expanded the coverage of preferences to include
ListPreferences with dynamic contents (generated in Java on the fly,
not hard-coded as resources)
Added more material on
StreamProvider, as an alternative to
Retired the tapjacking chapter, migrating the relevant portions of that
material to the chapter on miscellaneous security techniques
Deleted obsolete chapters on ActionBarSherlock and non-Android Wear
Removed material on ViewPagerIndicator
The APK edition of the book has a few more appinars in
the Community Theater, including:
- Doing Periodic Work
- Consuming Content from a
- Notification Basics
- Lockscreen Notifications
Version 7.4 is tentatively slated for early June and will include material
on whatever fun stuff Google will announce at Google I|O later this month.
—May 02, 2016
Your App May Be Getting on the ARC
Ars Technica is reporting that
it appears that a recent Chrome OS update has the underpinnings of making the Play Store and its contents available to users.
It was just over a year ago that
I blogged about the App Runtime for Chrome, or ARC.
This was nascent technology from Google that would allow you to package
your app as a Chrome extension and run it on Chrome OS (and, for testing
purposes, on Chrome for traditional desktop operating systems). When I
spent some more time with it earlier this year, to see what Google may
have improved with ARC, my sense was that it was merely an experiment,
Ars’ reporting turns that on its head. The language that they and various
Redditors uncovered suggests that a substantial chunk of the Play Store
catalog will be available to Chrome OS users. There is absolutely no
chance that the developers of “over a million apps and games on Google Play”
have manually tested their wares on ARC. This would indicate that Google
is taking an opt-out approach: your app will be available on Chrome OS
unless you do something (in the app or in the Play Developer Console)
to block it.
The ARC environment is a strange world for an Android app.
Your app runs in an SDK-style emulator, but where
the AVD contains no other apps, including few of the standard system
apps that you see when you create an AVD through the AVD Manager. Each
app is in its own silo, so even if the Chrome OS user has three apps
installed, each app is isolated from the others.
Now, the details of how ARC works may shift as part of this rollout, and
I think there is a decent chance that we will learn more at Google I|O in
a few weeks.
What does this mean for you, as an Android app developer?
Pay close attention to discussion about ARC in and around Google I|O.
If it looks like what Ars is describing is coming true, pay very
close attention to whether your app will automatically be distributed
to Chrome OS devices. If it will be, either nab a Chromebook and test
your app, or follow whatever instructions they provide to block your app
from Chrome OS until you are in position to do such testing. FWIW,
my book has a chapter on ARC and what it means for your app.
Pay further attention to where Android apps can run with ARC. If
the answer is “just” Chrome OS, that audience probably is on par, roughly,
with the number of people using Android 2.x today. If, on the other
hand, Google makes this available to all Chrome users, that would be a
massive audience. My guess is that they will do this in two stages, with
Chrome OS first, followed by desktop Chrome support in 2017 or beyond, but
that is just a guess. You may not care enough about Chrome OS users
to spend much time tuning your app for ARC, but you really should care
about all Chrome users.
This would seem to be independent of Android N’s multi-window feature.
Freeform multi-window seems specifically aimed at an Android-based
environment that has several windows. Perhaps I am misinterpreting what
we are being told. However, if I am correct, it would seem that Google
may be preparing a multi-prong dive into competing with traditional
desktop operating systems, with a more desktop-y Android to go along
with an expanded Chrome OS.
An opt-out ARC, along with opt-out multiwindow support
(to the extent that opting-out even works),
suggests that Google is tired of the modest developer adoption of
Android TV, Android Wear, and Android Auto. Where they think that they
can get away with it, Google may aim to make the Play Store catalog available
in toto to new environments, much as they did with tablets back in 2011.
I cannot say that I find Ars’ reporting to be surprising, considering
that my book’s chapter on desktop Android suggested that an
expanded ARC would be one of Google’s moves. However, this puts added
emphasis on paying close attention to what is announced at Google I|O,
to any updated developer previews or support libraries released at I|O,
and so on.
—Apr 25, 2016