The CommonsBlog

"Elements of Android Jetpack" Version 0.8 Released

Subscribers now have access to Version 0.8 of Elements of Android Jetpack, in PDF, EPUB, and MOBI/Kindle formats. Just log into your Warescription page to download it, or set up an account and subscribe!

This update adds three new chapters:

It also updates a bunch of the dependencies and tweaks the material to match, such as replacing the deprecated ViewModelProviders.of() with new ViewModelProvider() or by viewModels(). Similarly, the material on saved instance states in the chapter on processes now uses the SavedStateHandle approach with ViewModel.

And, as usual, it fixes lots of bugs.

Feb 24, 2020

Random Musings on the R Developer Preview 1

Each time Google releases a new developer preview, I rummage through the API differences report the high-level overviews, and even the release blog post, to see if there are things that warrant more attention from developers. I try to emphasize mainstream features that any developer might reasonably use, along with things that may not get quite as much attention, because they are buried in the JavaDocs.

As with the previous three releases, R seems to be lacking a prominent user-facing feature. I have no idea what I will tell my mother that she’ll gain if her phone gets the R update at some point.

Even the changes for developers seem relatively modest this time around. It’s almost like Android is maturing as a platform!

That being said, here are some things that caught my eye.

What May Make Users Unhappy

The phrasing around the file and directory access restrictions puts the blame on developers, suggesting that we ask users to grant us access Downloads/ document tree, or for files in Android/data/ directories.

In reality, developers don’t do that. Developers simply fire off ACTION_OPEN_DOCUMENT, ACTION_OPEN_DOCUMENT_TREE, or ACTION_CREATE_DOCUMENT Intents. Where the user winds up choosing is up to the user.

So, the restrictions listed in this section will be placed on users, not developers. I do not know what sort of messaging Google could use in the Storage Access Framework UI that would justify to the user blocking them from navigating to certain portions of external and removable storage. “Sorry, you can’t choose Downloads/, because ¯\_(ツ)_/¯” doesn’t strike me as something that will make users very pleased.

I am all for improved user control over privacy and security concerns, but the boundaries between what is and is not allowed need to make sense to users and, secondarily, to developers.

What Will Irritate Developers

On the one hand, the “All Files Access” support should be a good thing. It gives file managers and similar sorts of apps an option for accessing more stuff on external and removable storage. However, it is not really “all files”, as Android/data/ is still blocked off. IMHO, the next step is to use the one-time permission system to allow these apps to have access to these protected areas (e.g., so a backup app can actually do a backup).

The background location restrictions are confusing as documented. I’ll be playing around with these and hopefully will work out some samples soon.

What Got Better

The batch media file access and access to media via “raw file paths” represent welcome improvements to the scoped storage restrictions added in Android 10.

The data access auditing – allowing you to track where you are accessing sensitive data and by what app feature – is promising, albeit confusing. I’m sensing a book chapter in my future…

The shared datasets feature, for apps sharing large quantities of static data – is a nice addition, but it is one of those things that may be difficult to backport. And, without a backport, its utility will be limited until Android 11+ is dominant, which will take years.

Resource loaders is a hugely useful addition, so we can create an officially-supported solution for updating resources and assets without shipping a new app update. I can see appcompat gaining a backport of this.

Conversations are interesting if somewhat narrow in effective scope.

What’s Old and New Again

In summary:

What’s Obscure But Cool

We can get a list of “historical process exit reasons”, including crashes, low-memory pressure, permission changes, etc. IMHO, there should be a unique identifier on these, but even without it, having this data can be very useful for understanding what’s going on with the app in production.

There are a new series of methods on MediaStore for creating PendingIntent objects that perform actions against the store itself, such as marking items as favorites or deleting items. These could be attached to notification actions, app widget buttons, and other places where we need a PendingIntent, and we don’t have to fuss with our own code for implementing them.

What Has Me Worried

The documentation around one-time permissions is too limited. In particular, the explanation of the lifetime of the permission grant is unclear. Some developers claim that you need to re-request the permission even after onPause() of some activity, whereas in light testing I’m finding that the permission grant lasts for the entire process lifetime.

There is a new QUERY_ALL_PACKAGES permission, which “allows query of any normal app on the device, regardless of manifest declarations” (emphasis added). I worry about what it means for an app to declare itself as not being visible via PackageManager.

What’s Dead

Google is continuing the trend of deprecating highly-visible classes that have better replacements nowadays. In Android 11, this includes:

  • AsyncTask: use coroutines, RxJava, custom executors, etc.

  • IntentService: use JobIntentService or a regular Service

  • ListActivity and ExpandableListActivity: use a RecyclerView

  • TabHost and TabWidget: there are a million tab solutions out there, pick one

Also, they deprecated TimingLogger and LauncherActivity. I rarely used TimingLogger and might never have known about LauncherActivity.

setView() on Toast is dead. If you are using this, it will stop working once your targetSdkVersion reaches R.

And, we can no longer get official translations of “yes” and “no”, inexplicably.

What Makes Me Go “Hmmmmm”

As usual, there is a bunch of stuff that shows up in the API differences report that is interesting, confusing, and sometimes both:

What Happens Now?

I am working on Elements of Android R and should have a 0.1 version out for subscribers within a few weeks. And, I will be continuing to blog any scary findings (scary bad or scary cool) as I stumble upon them.

Feb 21, 2020

Scoped Storage Stories: The Undocumented Documents

Android 10 is greatly restricting access to external storage via filesystem APIs. Instead, we need to use other APIs to work with content. This is the 12th post in a seemingly never-ending series, where we will explore how to work with those alternatives.

One apparent gap in the MediaStore options is the Documents/ directory. We have obvious support for the media directories and Downloads/, but everything else had to be handled via the Storage Access Framework. This includes Documents/, which, like Downloads/, seems like it would be a general-purpose location.

As it turns out, there is support for Documents/. It’s just not documented.

This comes to us courtesy of Stack Overflow user blackapps, and this answer.

To work with downloads, as we saw previously, we can use MediaStore.Downloads as the basis. However, using MediaStore.Files, we can work with Downlodads/ and Documents/, plus custom subdirectories under those.

Specifically, MediaStore.Files.getContentUri("external") gets us a Uri that we can use akin to MediaStore.Downloads.EXTERNAL_CONTENT_URI for inserting new content and querying for our own content, though as before we cannot access other apps’ content this way. If we use RELATIVE_PATH and specify Documents, our content will go into the Documents/ directory:

val values = ContentValues().apply {
  put(MediaStore.Files.FileColumns.DISPLAY_NAME, filename)
  put(MediaStore.Files.FileColumns.MIME_TYPE, mimeType)
  put(MediaStore.Files.FileColumns.RELATIVE_PATH, "Documents")
  put(MediaStore.Files.FileColumns.IS_PENDING, 1)

val resolver = context.contentResolver
val uri =
  resolver.insert(MediaStore.Files.getContentUri("external"), values)

uri?.let {
  resolver.openOutputStream(uri)?.use { outputStream ->
    val sink = outputStream.sink().buffer()

    response.body?.source()?.let { sink.writeAll(it) }

  values.put(MediaStore.Downloads.IS_PENDING, 0)
  resolver.update(uri, values, null, null)
} ?: throw RuntimeException("MediaStore failed for some reason")

We can also use subdirectories under Documents/, such as Documents/AwesomeStuff. And, we can use Downloads/ as the base. Attempts to write to other root directories than Documents/ and Downloads/ will fail with an error.

You can even use this for removable volumes. blackapps’ answer shows a hard-coded storage volume ID, though presumably using StorageManager and methods like getStorageVolumes() will be more flexible.

The fact that this does not appear to be documented means that it is possible that this will not be supported in future versions of Android. So, use this approach with caution. But, if Documents/ is a must-have location, and you really want to avoid the Storage Access Framework, MediaStore.Files may be something to consider.

The entire series of “Scoped Storage Stories” posts includes posts on:

Feb 15, 2020

"Elements of Android Room" Version 0.2 Released

Subscribers now have access to Version 0.2 of Elements of Android Room, in PDF, EPUB, and MOBI/Kindle formats. Just log into your Warescription page to download it, or set up an account and subscribe!

This update brings with it four new chapters, on:

Plus, there is your usual set of bug fixes and other minor tweaks.

The next update to this particular book is tentatively slated for April, as I want to update some of the other books first. Next up: wrapping up the major work on Elements of Android Jetpack.

Feb 10, 2020

GDSA for App Distribution: Promising, Yet Problems

Making the news this week is the Global Developer Service Alliance (GDSA), an initiative of Oppo, Vivo, Xiaomi, and (maybe) Huawei. It aims to provide a central site for developers to be able to submit apps to all of the partners’ app distribution channels. And, with an English-language site option, an apparent objective is to help encourage developers from beyond China to publish in those app distribution channels.

This sort of “write once, submit anywhere” approach for app distribution is certainly welcome. I remember delivering an ah hoc presentation on the topic at droidcon UK nearly a decade ago. IMHO, a more powerful solution would be to have a standardized app distribution API, perhaps reminiscent of Google’s for the Play Store. Perhaps that will be added in the future. Still, a central site is better than nothing.

That being said, the site in its current state… well, let’s just say that it needs work. Perhaps it was announced before it was ready — the Reuters article calls it a “prototype”. Plus, for this sort of thing to succeed, beyond the actual site technology, you need the developer advocates who can help with the site content, developer support, and overall evangelism. Right now, I do not see any sign of that.

GSDA has a tough road ahead of it, but it is worth keeping an eye on, particularly if they expand to include other app distribution channels beyond the manufacturer-specific ones currently in the alliance.

Feb 08, 2020

Older Posts