Here’s how Android’s new Photo Picker can support apps that haven’t been updated
Thanks to Project Mainline, AOSP has become modular enough that key features introduced with the latest Android release can be rolled out to devices running older versions of the OS. There are a lot of caveats that I’m glossing over here, but before Mainline came around, the only way for new Android features to be backported to older releases was through Google Play Services, which is closed source and not available through AOSP. Through a combination of Project Mainline and Google Play Services updates, the new Photo Picker Google introduced with Android 13 is available on devices running versions of Android as old as far back as Android 4.4.
That means that the new Photo Picker, by Google’s own metrics, is available on nearly 99.7% of all Android devices out there. Keep in mind that Google rarely updates its Android version distribution statistics and that they only track the number of GMS Android devices. Still, with Google reporting over 3 billion active Android devices, that means a lot of users have access to this new feature.
But I bet most of you have never used this feature, and you’d be hard pressed to find anyone who has. I can count the number of apps that support the new Photo Picker in one hand. The Photo Picker might be a relatively simple feature, but any time a feature requires developers to add support for it, you have to patiently wait for them to add support. Some developers may evaluate the feature and feel it isn’t something they want to add, some add support pretty quickly, and some have to do a lot of careful testing before adding support. The time frame depends on the individual project, developer(s), and/or companies working on the project.
To speed up adoption of this new feature, some existing apps that use the old system file picker will soon be automatically switched over to the new Photo Picker, without any code changes needed. This means that you’ll see the new Photo Picker in some apps that haven’t even been updated to support it yet! How exactly Google is going about switching apps from the old system file picker to the new Photo Picker is quite interesting, so in this edition of Android Dessert Bites, I’ll be sharing the under-the-hood details behind this switcheroo.
Before I dive into the details, though, I’m going to have to explain how the Photo Picker actually works. And by that I mean I’ll point you to this deep dive I wrote a few months back explaining everything you need to know about the Photo Picker. The article explains what it does, how it works under-the-hood, how it’s distributed, how it differs from the old system file picker, how to try it out, and what’s coming in future updates. The article also featured my speculation that Google was going to backport the Photo Picker to older versions of Android via Play Services, but that has since been confirmed in the most recent revision of the Google System Updates support page.
Since I published that article, Google quietly announced that they are “bringing the benefits of [the] Android photo picker to existing apps … by switching the system file picker for the photo picker under the hood without any code change required in the coming months.” The news was buried in a blog post on Medium written by Yacine Rezgui from the Android Developer Relations team. As he explains in the post, existing apps invoking the ACTION_GET_CONTENT intent with an image and/or video mime type filter will be shown the Photo Picker instead of the system file picker. The app will then receive a list of URI(s) of the file(s) that were picked, which is exactly the result they’re already expecting. From the app’s point of view, nothing has changed, but the user benefits from being able to use the Photo Picker to pick media items.
The key change that needs to be made for this switcheroo to happen is having the Photo Picker respond to the ACTION_GET_CONTENT intent. Currently, DocumentsUI (the app behind the system file picker) responds to any invocation of the ACTION_GET_CONTENT intent, and it has done so for every Android release since Android 4.4.
The first step to take over this intent is to add an intent filter to the Manifest of MediaProvider (the app behind the Photo Picker) to tell the system that the Photo Picker activity should respond to the ACTION_GET_CONTENT intent. This intent filter could’ve been added to the existing Photo Picker activity entry in the Manifest, but Google chose to add a separate activity alias tag with an intent filter for ACTION_GET_CONTENT. The reason this was done was likely so Google could control the rollout of the ACTION_GET_CONTENT intent takeover behind a feature flag, but I’ll get into that in a bit.
With some exceptions, when the system identifies that there are two or more activities that can handle the same intent, the system will show a disambiguation dialog to let the user select which app to use. It doesn’t make sense for the system to let the user pick between DocumentsUI and MediaProvider, though, as most users won’t know the difference. In fact, it doesn’t make sense for the system to let the user pick the handler of ACTION_GET_CONTENT at all, as doing so opens up the possibility for abuse or broken behavior. (What if a third-party app declares it supports the ACTION_GET_CONTENT intent and appears in the disambiguation dialog?)
To enable the prioritization of system apps over third-party apps for certain intents, Android supports defining the priority of an intent filter. The parent component of the intent filter with the highest priority is called before any others. Since Android 7.0 Nougat, though, only privileged applications can request a priority > 0. (A “privileged” application is a system app that resides in a priv-app directory in one of the system image partitions of the device firmware.) Android caps the priority of non-privileged applications to 0, ensuring that any components of privileged applications that have a high priority defined in an intent filter tag will be run first.
The priority of the intent filter telling the system that DocumentsUI should handle ACTION_GET_CONTENT is ‘100’. By defining the priority of the intent filter that tells the system to let MediaProvider handle ACTION_GET_CONTENT to be ‘101’, the system will choose MediaProvider over DocumentsUI when the ACTION_GET_CONTENT intent is invoked by an app because the former has a higher priority. It really is that simple!
There are a few details that I glossed over, such as the fact that the ACTION_GET_CONTENT intent filter in MediaProvider only catches intents with the “image/*”, “video/*”, and “*/*” MIME types. Even though “*/*” isn’t defined in the intent filter, it’s caught because it’s a superset of “image/*” and “video/*”. The “*/*” MIME type being caught is a problem because it means an app that wants to invoke the system file picker to let the user pick a file of any type will instead be limited by the file types supported by the Photo Picker (images and videos). To work around this, MediaProvider reroutes the intent to DocumentsUI when the intent specifies the “*/*” MIME type.
I mentioned earlier that this ACTION_GET_CONTENT takeover is gated behind a feature flag, and that’s because Google wanted to test this feature before rolling it out. The Photo Picker activity that responds to the ACTION_GET_CONTENT intent is disabled by default currently but can be enabled through a Device Config flag. Simply run the following shell command to enable this feature:
cmd device_config put storage_native_boot take_over_get_content true
This only works on MediaProvider versions 330811020 or later, however. I don’t know which Google Play System Update that version of the module was rolled out with, but you can check the version that’s available on your device with the following shell command:
pm list packages --apex-only --show-versioncode | grep com.google.android.mediaprovider
Then, find an app that invokes the old system file picker to pick photos or videos, such as Google Keep. Google Keep is a great example because the option to pick photos isn’t locked behind a permissions dialog. Other apps like Twitter or Slack also work to showcase this ACTION_GET_CONTENT takeover, but both apps require you to grant them media permissions before you can access the Photo Picker, defeating one of the biggest reasons to use it.
(Since Google Play Services is also a privileged app, I think it should be possible for the GMS-provided Photo Picker to take over the ACTION_GET_CONTENT intent on older versions of Android. However, this hasn’t been implemented yet as it likely needs further testing by Google. Thus, for now, Photo Picker taking over the ACTION_GET_CONTENT intent will only happen on Android 11+ devices that support Project Mainline.)
Thanks for reading another edition of Android Dessert Bites! I try not to repeat the same topic in this column, but I hope you found this deep dive illuminating! It was interesting finding out how such a simple change to the Manifest enables apps to support the Photo Picker without having to update a single line of code. If you liked this article, then check out my other posts in the Android Dessert Bites series: