Rotate suggestions

In Android 8.0, users could toggle between auto-rotate and portrait rotation modes using a Quicksettings tile or Display settings. In Android 9, we updated portrait rotation mode to eliminate unintentional rotations by pinning the current screen rotation even if the device position changes. Users can trigger rotation manually when needed by pressing a new button in the navigation bar. We renamed the portrait mode to rotation lock and it activates when auto-rotate is off. There are no changes to auto-rotate mode.

When the device is in rotation lock mode, users can lock their screen to any rotation supported by the top, visible Activity (given current system constraints). If the top Activity can be rendered in multiple rotations in auto-rotate mode, the same options should be available in rotation locked mode, with some exceptions based on the Activity's screenOrientation setting.

Rotation lock mode works by showing a button in the navbar on device rotation changes. To accomplish this, the device's orientation sensor must remain active even when auto-rotate is off. Tapping this button effectively sets user rotation preference (Settings.System.USER_ROTATION). WindowManager uses this preference, along with other details about the top Activity and system status, to change the system's rotation. WindowManager continues to use user rotation preference when deciding what rotation to render the system in when moving to another Activity.

This gif shows a phone in landscape orientation with the screen in
       portrait orientation. An icon appears to ask the user if they want to
       change their screen orientation to landscape.
Figure 1. Rotate suggestion button with "Swipe up on Home button" gesture enabled

User rotation preference should be maintained when moving between Activities. However, because most phone users only want to be in landscape for a short, temporary period of time, we added natural orientation bias. User rotation preference is reset to the device's natural orientation whenever the system rotation changes to the device's natural orientation. For most phones, the device's natural orientation is portrait (0º). Resetting user rotation preference often happens when using a portrait-only app, locking the phone or returning to launcher workspace.

Rotation interactions for users haven't changed much in the last decade. Users may find this feature hard to discover given their prior history with rotation and button positioning in the navigation bar. For this reason, we've added an introduction mode to the rotate button that highlights it when it appears. Intro mode behavior only happens for the first few button interactions after which introduction mode is disabled.

Source

Support for rotation suggestions has been added to Android 9. Most changes are contained within the following files.

  • services/.../server/policy/PhoneWindowManager.java:
    • Hooks consuming the output of WindowOrientationListener (MyOrientationListener, responsible for monitoring sensors to determine if the device has been rotated)
    • Keeps the WindowOrientationListener active even when auto-rotate is disabled (see needSensorRunningLp())
    • Computes the system rotation given user rotation preference, top Activity screenOrientation settings and system status (see rotationForOrientationLw())
    • Determine if the top Activity can rotate to a given rotation (see isRotationChoicePossible())
  • SystemUI/.../statusbar/phone/NavigationBarFragment:
    • Determines if the navbar button should be shown on rotation suggestion callbacks from PhoneWindowManager (see onRotationProposal())
    • Handles when to hide the rotate navbar button (see calls to setRotateSuggestionButtonState(false))
    • Handles button timeouts, including the special case when the navbar is hidden (commonly in full screen)
    • Resets user preference on return to the device's natural orientation (mRotationWatcher)
    • Picks the appropriate style for the navbar button animation, applied in NavigationBarView (see onRotationProposal())
    • Adds introduction mode logic, including specialized animation (see references to Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED)
    • Implements the disable2 rotation flag (see disable())
  • SystemUI/.../statusbar/phone/NavigationBarView.java:
    • Styles button icon animation to match pending rotation (see updateRotateSuggestionButtonStyle())
    • Handles button visibility changes (see setRotateButtonVisibility()), including logic to hide the rotate button if certain Accessibility services are active (accounting for the right-most navbar button stack ranking)
  • SystemUI/res/layout/menu_ime.xml:
    • Includes a new KeyButtonView for the rotate button, stacked above the menu and IME/keyboard chooser but below the Accessibility button
  • SystemUI/res/drawable/ic_sysbar_rotate_button.xml:
    • Complex AnimatedVectorDrawable used to animate the rotate navbar button
    • Styling (in SystemUI/res/values/styles.xml) is used to set the start and end angles of rotation so the same drawable can be used to animate various starting and ending rotations
    • Icon tinting is set via TintedKeyButtonDrawable

Implementation

Android 9 includes all necessary changes to get rotation suggestions working for devices that use software navigation keys (back, home, etc).

Device manufacturers who create devices with hardware navigation keys that wish to implement this feature will need to design and implement their own System UI affordance or disable the feature. It is recommended that any introduced surface be easy to use when the device is held at 90º or 180º to the current system rotation and is rapidly accessible. For these reasons, the use of notifications (as is done for the IME/keyboard picker) is not recommended.

The hardware requirements to use this feature are the same as the requirements to use auto-rotate.

It is necessary for implementation consistency that user rotation preference (Settings.System.USER_ROTATION) is reset to the device's natural rotation when the system changes to the device's natural rotation for any reason when auto-rotate is off. The provided implementation does this (see NavigationBarFragment.mRotationWatcher).

There is a new flag in StatusBarManager.disable2 to temporarily prevent rotation suggestions from appearing. See StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS. This flag must be respected in all implementations as it's used by critical system apps, including Setup Wizard. The provided implementation supports this (see NavigationBarFragment.disable()).

We strongly recommend enabling the feature and following the AOSP implementation, if possible. We aim to keep the rotation experience similar between devices, mirroring the uniformity in experience on most phones today between auto-rotate and portrait lock.

Customization

As rotation suggestions appear only in rotation locked mode (auto-rotate off), it is possible to choose if the feature is default on for new installs by choosing to have auto-rotate off by default. See def_accelerometer_rotation in SettingsProvider/res/values/defaults.xml to make default changes.

Users can easily change if auto-rotate is active or not (regardless of default) via the rotate tile in Quicksettings or Display settings.

Validation

For testing, the feature can be turned off and on by altering a gating Settings.Secure value. This accomplished easiest by running the following command from a privileged adb instance:

adb shell settings put secure show_rotation_suggestions <x>

Set x to 0 for off and 1 for on.

For testing, introduction mode can be reset by altering the associated Settings.Secure value. This accomplished easiest by running the following command from a privileged adb instance:

adb shell settings put secure num_rotation_suggestions_accepted 0