Register now for Android Dev Summit 2019!

Gestural navigation

Android Q introduces an option for fully gestural system navigation. There are two things that app developers should do to prepare for this feature:

  • Extend app content from edge to edge.
  • Handle conflicting app gestures.

Edge-to-edge app content

In order to take advantage of the additional screen space made available by the floating navigation bar, you'll need to make some changes to your app.

Setting transparent system bars

You can do this by setting the following values in your theme:

<!-- values-29/themes.xml: -->

<style name="AppTheme" parent="...">
    <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>

Alternatively, you can do this dynamically by using Window.setNavigationBarColor() and Window.setStatusBarColor().

Setting UI visibility flag

To be able to lay out your view edge-to-edge, your app must tell the system that the app can handle such a view. You can accomplish this using View.setSystemUiVisibility() to set the following flags:

Kotlin

view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

Java

view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

Together, these flags tell the system that your app should be laid out fullscreen, and as if the navigation and status bars were not there. For additional full-screen events, you can also set SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, which allows you to draw behind the status bar.

If you are using a view class such as CoordinatorLayout or DrawerLayout that automatically handles the status bar, the SYSTEM_UI_FLAG_LAYOUT_STABLE and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flags may already be set. Also, if you are using setSystemUiVisibility() to set other flags, such as SYSTEM_UI_FLAG_IMMERSIVE, you should be careful that those other flags do not overwrite the ones referenced above.

Even if your app uses an edge-to-edge view, the system still uses the WindowInsets API to indicate where the system bars are.

Consuming insets manually

If your app uses a custom view hierarchy, you may need to consume system window insets manually. You typically do this by implementing an OnApplyWindowInsetsListener interface:

Kotlin

view.setOnApplyWindowInsetsListener() {v, insets ->
    insets.consumeSystemWindowInsets()

Java

view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
    @Override
    public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
        // 1. Move views on top edge down by insets.getSystemWindowInsetTop()
        // 2. Move views on bottom edge up by insets.getSystemWindowInsetBottom()
        // 3. Also check getSystemWindowInsetLeft/Right()
        //    (i.e landscape orientations)
        return insets.consumeSystemWindowInsets();
    }
});

WindowInsets provides regular visual insets for all system bars through getSystemWindowInsets(). In addition, Android Q adds the following methods to WindowInsets:

Handling conflicting app gestures

The gestural navigation model may conflict with gestures that were previously used by app developers. You may need to make adjustments to your app's user interface as a result.

Conflicts with Back gestures

The new system gesture for Back is an inward swipe from either the left or the right edge of the screen. This may interfere with app navigation elements in those areas. To maintain functionality of elements on the left and right edges of the screen, you'll need to opt out of the Back gesture selectively by indicating to the system which regions need to receive touch input. You 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-dev01.

For example:

Kotlin

var exclusionRects = listOf(rect1, rect2, rect3)

fun onLayout(
        changedCanvas: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
  // Update rect bounds and the exclusionRects list
  setSystemGestureExclusionRects(exclusionRects)
}

fun onDraw(canvas: Canvas) {
  // Update rect bounds and the exclusionRects list
  setSystemGestureExclusionRects(exclusionRects)
}

Java

List<Rect> exclusionRects;

public void onLayout(
        boolean changedCanvas, int left, int top, int right, int bottom) {
    // Update rect bounds and the exclusionRects list
    setSystemGestureExclusionRects(exclusionRects);
}

public void onDraw(Canvas canvas) {
    // Update rect bounds and the exclusionRects list
    setSystemGestureExclusionRects(exclusionRects);
}

Conflicts with Home/Quick Switch gestures

The new system gestures for Home and Quick Switch both involve swipes at the bottom of the screen in the space previously occupied by the nav bar. Apps cannot opt out of these gestures, as they can with the Back gesture.

To mitigate this problem, Android Q introduces the WindowInsets.getMandatorySystemGestureInsets() API, which informs apps of the touch recognition thresholds.

Games and other non-View apps

Games and other apps that do not have a view hierarchy often require the user to swipe near the system gesture areas. In those cases, games can use Window.setSystemGestureExclusionRects() to exclude areas that overlap with areas reserved for system gestures. Games should make sure to only exclude these areas when necessary, such as during gameplay.

If a game requires the user to swipe near the home gesture area, the app can request to be laid out in immersive mode. This disables the system gestures while the user is interacting with the game, but allows the user to re-enable the system gestures by swiping from the bottom of the screen.