Other Changes of Note
There are lots of other changes in Android 11, far more than can be presented in this book. This chapter covers a variety of additional changes that you may want to pay attention to.
Stuff That Might Break You
The scariest batch of changes in any Android release are the ones that may break existing app behavior. Things like package visibility might qualify for that.
Here are a few other smaller changes that may cause problems for reasonably-ordinary apps.
Dismissable Ongoing Notifications
For your foreground services, you may be used to raising “ongoing” notifications. These normally are not dismissable by the user.
However, in Android 11, they are.
On the plus side, this does not appear to affect your process importance. You are still registered as having a foreground service, even if the user gets rid of your notification.
However, if you were used to that notification always being there to give the user control over that background work… now the user might remove that notification, intentionally or accidentally. Make sure that your app can still function reasonably, from a UX standpoint, if the user dismisses your notification.
Phone Number Permissions
Some Android SDK methods let you attempt to get the phone number of the device, such as getLine1Number()
on TelephonyManager
. In practice, these are not very reliable, but you are welcome to try to use them.
For years, in order to call those methods, you needed the READ_PHONE_STATE
permission. This is a dangerous
permission, and you needed to use runtime permissions to further request it on Android 6.0+.
In Android 11, once your targetSdkVersion
reaches 30, you need to request a different permission for those methods: READ_PHONE_NUMBERS
. This too is a dangerous
permission.
As a result, you need to decide which of those permissions you need based on API level, and include the correct permission in the array that you pass to requestPermissions()
.
If the only reason you were requesting READ_PHONE_STATE
was to use these methods, you may find that you no longer need it on Android 11+ devices. If so, you could elect to add android:maxSdkVersion="29"
to your <uses-permission>
element for READ_PHONE_STATE
to drop it off for API Level 30 and higher devices:
<uses-permission android:name="READ_PHONE_STATE" android:maxSdkVersion="29" />
Conversely, if you are using READ_PHONE_STATE
for other things, you will need to request both READ_PHONE_STATE
and READ_PHONE_NUMBERS
on API Level 30 devices, but only READ_PHONE_STATE
on older devices.
Overlay Tweak
Hopefully, you are not using the SYSTEM_ALERT_WINDOW
permission in your app. If you are, then you probably have noticed that Google is continuing to “tighten the screws” with each passing release. While it is possible that they will never outright ban SYSTEM_ALERT_WINDOW
, they certainly seem intent upon making it more aggravating for developers and users.
In Android 11, the change is to ACTION_MANAGE_OVERLAY_PERMISSION
.
SYSTEM_ALERT_WINDOW
is one of those special permissions that does not go through the standard dangerous
runtime permission system. Rather, the user needs to go into the “Special app access” section of the Apps screen in Settings, and from there go into “Display over other apps”. There, the user can tap on an app that is requesting SYSTEM_ALERT_WINDOW
and elect to grant or reject that permission for that app.
ACTION_MANAGE_OVERLAY_PERMISSION
is an Intent
action that allows you to send the user to this place in Settings. In Android 6.0 through 10, there were two ways to use this Intent
:
- On its own, to lead the user to the “Display over other apps” screen
- With a
package
Uri
tied to your application ID, to lead the user straight to the screen where they can grant (or deny) that permission for your app
In Android 11, that latter option is no longer available. You can provide the Uri
, but it will be ignored by the Settings app. The user is always taken to the “Display over other apps” screen, where the user will need to click on your app, then grant the permission.
No Third-Party Image Capture Support
ACTION_IMAGE_CAPTURE
is a popular means for an app to take a picture, by asking a camera app to do the “heavy lifting”. This means that the app can skip all of the headache of setting up a camera itself (e.g., via the CameraX library) and does not need the CAMERA
permission. There are also ACTION_IMAGE_CAPTURE_SECURE
and ACTION_VIDEO_CAPTURE
Intent
actions that perform similar operations.
The problem is that pre-installed camera apps often do not test these actions much, so they tend to have bugs. In lieu of dumping these actions and doing the camera work directly in the app, many apps simply guide the user to install a third-party camera app, one that is known to have a good implementation of things like ACTION_IMAGE_CAPTURE
. Then, when the app invokes the ACTION_IMAGE_CAPTURE
Intent
, the user could choose the third-party camera app in the chooser.
That is no longer an option on Android 11, once your targetSdkVersion
reaches 30.
Those three Intent
actions will only start a pre-installed camera app. User-installed camera apps are ignored. Even if the user has disabled all pre-installed camera apps, user-installed camera apps are still ignored — Android throws an ActivityNotFoundException
instead.
At minimum, if you are using these Intent
actions, you will need to handle the ActivityNotFoundException
scenario. You really needed that anyway, as work policies or similar constraints might have caused that Intent
to fail anyway.
Even if you try enabling package visibility for your desired Intent
action, you will find that third-party apps are ignored.
An explicit Intent
works for the startActivity()
/startActivityForResult()
call, if you happen to know of a camera app to try (e.g., net.sourceforge.opencamera
for Open Camera). This also works with queryIntentActivities()
on PackageManager
, if you also enable package visibility, so you can determine whether the Intent
would succeed or not before trying to start the activity.
The CamChooser
sample module in the book’s sample project demonstrates a related approach: adding candidate camera apps to a chooser:
package com.commonsware.android.r.camchooser
import android.content.Context
import android.content.Intent
private val CAMERA_CANDIDATES = listOf(
"net.sourceforge.opencamera"
)
fun enhanceCameraIntent(
context: Context,
baseIntent: Intent,
title: String
): Intent {
val pm = context.packageManager
val cameraIntents =
CAMERA_CANDIDATES.map { Intent(baseIntent).setPackage(it) }
.filter { pm.queryIntentActivities(it, 0).isNotEmpty() }
.toTypedArray()
return if (cameraIntents.isEmpty()) {
baseIntent
} else {
Intent
.createChooser(baseIntent, title)
.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents)
}
}
The enhanceCameraIntent()
function will sift through the application IDs from CAMERA_CANDIDATES
and see if any appear to exist and support some Intent
action (e.g., ACTION_IMAGE_CAPTURE
). If there are some, they are attached to Intent.createChooser()
via EXTRA_INITIAL_INTENTS
. You can then use the returned Intent
with startActivityForResult()
. The result is:
- If the user only has a pre-installed camera app available, that app is launched directly
- If the user only has a matching third-party camera app installed, that app is launched directly
- If the user has both, a chooser appears
- If the user has none, you get an
ActivityNotFoundException
So, you still need to worry about ActivityNotFoundException
, but that was always the case with these Intent
actions. The user might be running in a restricted profile and lack access to any camera apps, for example.
This sample only shows one candidate camera app, that being Open Camera. Enterprising developers might create a broader list of candidate apps that could be detected and used. The overall CamChooser
sample app implements a testbed, to allow you to see whether these media capture Intent
actions appear to work properly for your selected camera app. The checks are rudimentary, mostly confirming that we did not crash and did get the expected result (e.g., Bitmap
returned to us, photo/video stored in the EXTRA_OUTPUT
location).
Maps V1 Removed
If your app is very old, it is possible that you are still trying to limp along with the original Google Maps on Android implementation, sometimes referred to as “Maps v1”. If you are using classes like com.google.android.maps.MapView
and have a <uses-library android:name="com.google.android.maps" />
manifest entry, you are using Maps v1.
And you need to stop. Maps v1 has been deprecated for quite some time, it stopped working in Android 10, and in Android 11, it is simply gone.
If you have <uses-library android:name="com.google.android.maps" android:required="false" />
, and you are checking at runtime for the existence of com.google.android.maps.MapView
(e.g., Class.forName("com.google.android.maps.MapView")
), your app should not find that class, and you should go through whatever sort of “graceful degradation” code path that you have set up for such devices.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.