Preparing your app for Android Q
Tags: android, androiddev, google
Published: 2019-07-14 05:11:43

We are into the 10th year of Android development (Android Q should be 10.0). As per Beta 4, Android Q is officially API level 29. Even though we have a Beta 5 and a Beta 6 is expected, the API's have been marked as final and now is a good time to see how Android Q will affect your app and what changes should be made to fully support Android Q.

The important changes (and not the full list) introduced in Android Q can be divided into two categories: a) Privacy & Security. b) User Experience


---

##1) Privacy & Security
####a) Background Activity Starts
If you try to start an activity, while your app is in the background, it will no longer be allowed.

**Affects:** All apps running on Q (irrespective of target SDK). An exception is thrown if the app targets Android Q while the activity just doesn't launch if the app doesn't target Android Q but is running on an Android Q device.

**Exceptions:** Bound services such as accessibility, auto-fill, etc. If the app receives a `PendingIntent` from the system, we use can use it to launch an activity. If the app has `SYSTEM_ALERT_WINDOW` permission (removed in the Android GO edition) or the app called `finish()` on an activity very recently (would not recommend relying on this. The idea of "recently" can be very dubious), then your app is exempted from this restriction as well.

**Recommended approach:** Notification Triggered activity

{% gist https://gist.github.com/saurabharora90/a5167d53385876dfcd73ce74e1904792 %}


So if we want to launch an activity from the background, first create a Notification to display to the user. In that notification, add a Fullscreen pending intent. Also, add the `USE_FULL_SCREEN_INTENT` permission to your manifest. Now, when the notification is triggered, the system will launch the full-screen intent for you.

**Gotcha's:** System decides between when to show a notification and when to show the activity. If the user is actively using the device, then a heads up notification is displayed. If the device is ideal or when the user interacts with the notification, the full-screen activity is launched. Think of receiving a phone call (heads up notification while using the phone, otherwise full-screen activity).

####b) Hardware Identifiers
Access to non-resettable device identifiers has been revoked in Android Q.

**Affects:** All apps running on Q (irrespective of target SDK). An exception is thrown if Target SDK is Q and a null is returned if Target SDK < Q

**Avoid:** Mac Address is now randomized, while IMEI (`TelephonyManager.getDeviceId()`) and serial number are no longer accessible. They are now bucketed under "privileged permissions" and are only available for carrier apps.

**Recommended approach:** Use resettable identifiers such as Advertising ID, Instance ID or Globally-unique IDs (GUIDs). See [Best practices for unique identifiers](https://developer.android.com/training/articles/user-data-ids) for more details on which identifier to use for which scenario.

####c) Background Location
Beginning with Android Q, the system will distinguish between location requests made in the foreground vs background.

![The location permission request will now have 3 options: Allow all the time?. Allow only when using the app (just foreground access) and Deny (No access)](https://miro.medium.com/max/676/0*7IOuJFOmHcRJfSPc)

The location permission request will now have 3 options: Allow all the time?. Allow only when using the app (just foreground access) and Deny (No access)

**Affects:** Depends. If the app targets Q then you need to request for a new background location permission. If the app doesn't target Q, then it will automatically get this permission if it already had location access permissions.

![](https://miro.medium.com/max/1400/0*r0C-ormXfsTiqA73)

**Recommended approach:** If the app needs one-time access to the user's location to complete some tasks, use a foreground service with the [foregroundServiceType](https://developer.android.com/reference/android/R.attr.html#foregroundServiceType) set as `location` in the app's manifest file.

```
<service android:name="MyNavigationService"
android:foregroundServiceType="location" ... />
```

If the app needs access to the device location all the time such as for geofencing, then it can set up a request for background location permission. Other aspects of the app (such as how the location is fetched and used) don't need to change. To request access for background location, add the `ACCESS_BACKGROUND_LOCATION` permission to the manifest:


{% gist https://gist.github.com/saurabharora90/a7f6606e5d2e5318115c2c7dd284a690 %}


**Gotcha's:** 
![Reminder triggered by the system about app accessing location in the background] (https://miro.medium.com/max/676/0*36Cz5oh0AMROBWXF)
A couple of important things to be aware off: The user might receive a reminder after giving an app accesses device location in the background and just like any other permission, the user can revoke the background location permission that was granted to the app. This is especially critical for apps not targeting Q but running on Android Q devices since it would get the background permission by default if it had the location permission. Make sure the app gracefully handles such scenarios. For this reason, whenever the app starts a service or requests for location, check whether the user still allows the app to access location information.


---

##2) User Experiences
####a) Gesture Navigation
In Android Q, Google has tried to unify all the gesture navigations out there with a new gesture navigation system to replace the floating system navigation bar (back, home, and recents)

![](https://miro.medium.com/max/1400/0*7jJmcpK3ZUdL5_Qr)

**Affects:** So if your app contains gestures, then they can possibly conflict with the system gestures. This will affect all apps that run on Android Q (irrespective of target SDK) since the system gestures will get the first dibs on handling the gesture.

**Recommended approach:** Since the floating system navigation bar is effectively gone, it is recommended that apps now take full advantage of the new real estate available to them and provide an immersive experience to users.

To build a more immersive experience, apps should draw behind the status and the navigation bar. First, request the navigation and status bar to be transparent.

```
<style name="AppTheme">
......
<item name="android:navigationBarColor">@android:color/transparent</item>
<!-- Optional, but recommended for full edge-to-edge rendering -->
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>
```

Next, request your activity/view to be laid out full screen:

```
view.systemUiVisibility =
//Layout as if the navigation bar was hidden
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or
//Layout as if the status bar was hidden
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or
//Layout at its most extreme
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
```

Now that the app has been laid out in full screen and behind the (transparent) system components, it could be that some clickable component of the app overlaps with the system UI. 

![FAB overlapping with the floating navigation bar. Floating Navigation bar enabled for ease of clarity](https://miro.medium.com/max/578/1*dv6HSCsZ4k6qrXAVGnR-RA.png)

To resolve this, use Insets.

Insets are a collection of values that tell us how much to move our views by so that they don't conflict with the system UI. This information is provided by the [`WindowInset`](https://developer.android.com/reference/android/view/WindowInsets) class. Since API Level 20, Android has provided developers with System Window Insets, which tells us how much space is taken up by the system components (such as the floating navigation bar and the status bar). This information can be used to move the clickable views so that they can be clicked instead of the system UI.

{% gist https://gist.github.com/saurabharora90/711a8e4dcb54dcd8f48ae8a0f8ad6a34 %}

For the FAB, set up a listener to WindowsInsets, and apply the bottom window inset as a bottom margin.

Similarly, beginning with Android Q, the system exposes gesture navigation insets, to move the swipeable views so that they don't conflict with the system gestures. These are exposed through [`Insets.getSystemGestureInsets()`](https://developer.android.com/reference/android/view/WindowInsets.html#getSystemGestureInsets())

But what if we don't want to move the views and have some content in those gesture zones which might conflict with the app? 

![System Gesture insets region highlighted in yellow.](https://miro.medium.com/max/612/1*Epn1TlHY19Gi7uE_81tD1Q.png)

In those scenarios, the app can define regions in which the app will get to consume the touch event first and not the system gestures. It can do this by passing a `List<Rect>` to the `View.setSystemGestureExclusionRects()` API introduced in Android Q. This method is also available in `ViewCompat` as of `androidx.core:core:1.1.0`

**Gotcha's:** You can only opt out of the back gesture bounds. Home gesture bounds are reserved and cannot be overridden. This is because there is only one way to exit the app. If your app conflicts with the home gesture, use `WindowInsets.getMandatorySystemGestureInsets()` to get the bounds of the mandatory region and shift your views out of the region accordingly.

####b) Dark Theme
Over the last year or so, we have seen a lot of apps provide support for dark theme in their apps. Android Q brings this toggle to the system setting.

There are 3 ways a user can enable dark mode:
- A new system setting (Settings -> Display -> Theme)
- A new Quick Settings
- Depending on the manufacturer, through Battery Saver Mode.

In order to support the system-wide Dark Theme configuration, your app's default theme should inherit from the DayNight theme. This ties the app's main theme to the system-controlled night mode flags. This works by pulling the resources from the `night` qualifier folders.

```
<!--App Compat Style-->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!--Material Components-->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
```

If you look inside the App Compat styles, you'll notice that `DayNight` theme points to `AppCompat.Light` in the default configuration and points to `AppCompat` in the `night` configuration

```
<!--values/themes.xml-->
<style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat.Light">
<!--values-night/themes.xml-->
<style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat">
```

If you wish to provide a toggle to the users to control the theme from within the app, you can call [`AppCompatDelegate.setDefaultNightMode()`](https://developer.android.com/reference/androidx/appcompat/app/AppCompatDelegate#setdefaultnightmode), which takes in one of the four modes:

- `MODE_NIGHT_YES` - Show dark theme
- `MODE_NIGHT_NO` - Show Light Theme
- `MODE_NIGHT_FOLLOW_SYSTEM` - Follow System Settings
- `MODE_NIGHT_AUTO_BATTERY` - Dark theme when the device goes into power saving mode.

**Gotcha's:**

a) Don't hardcode colors. If there are hardcoded colors in layouts files, it's a red flag. Replace them with color references. Even better: use attributes to reference colors.

b) Check drawables and provide alternate resources for them under the `night` qualifier.

c) For vector drawables, don't have colors hardcoded in them. Use `tint` to style vector resources with the correct color.

d) Make sure all color resources have corresponding alternatives inside the `night` folder (values-night/colors.xml)

e) If the app uses `RemoteViews` for notifications or Widgets, remember to test them as well. If the app uses default Notification styles, then it should be good to go since the system will handle them.

f) Configuration Change for `uiMode` gets invoked whenever the device switches between normal and dark mode. The user might do that manually, or it might kick in when the device battery gets low. If the app does not handle configuration changes properly, the UI will lose context when the device switches between normal and dark mode, as the visible activities and their fragments get destroyed and recreated. So, even if the UI is locked to a single orientation, please add configuration change support. Also, if the app is manually managing configuration changes via `android:configChanges`, there is a good chance it'll want to manage uiMode manually as well. For example, if the app involves continuous playback (e.g., a video player), it might not want to interrupt playback just because the device switched between normal and dark mode.


---

A bunch of other things were also introduced in Android Q which I haven't covered in details such as `Settings Panel`, a new `AudioPlaybackCapture` API (an API gives apps the ability to copy the audio being played by other apps. This feature is the analog of screen capture, but for audio)

Lastly, Android Q includes an updated list of restricted non-SDK interfaces whose access while eventual be shut down from the Android SDK. You can find your list of violations in the play console under the Pre Launch Report.


---

If you feel there is something important that I missed out, let me know in the comment below.