旋转建议

在 Android 8.0 中,用户可以通过“快捷设置”图块或“显示”设置,在自动屏幕旋转模式和纵向旋转模式之间切换。在 Android 9 中,我们更新了纵向旋转模式,通过固定当前屏幕旋转方向来避免发生意外旋转(即使设备位置发生变化)。用户可以根据需要手动触发旋转,只需按一下导航栏中的新按钮即可。我们将纵向模式重命名为“旋转锁定”,它会在自动屏幕旋转模式关闭时启用。自动屏幕旋转模式没有任何变化。

当设备处于旋转锁定模式时,用户可以将其屏幕锁定为顶层可见 activity 支持的任何旋转方式(受当前系统限制约束)。如果顶层 activity 可在自动屏幕旋转模式下以多种旋转方向呈现,则旋转锁定模式下应提供相同的选项(根据 activity 的 screenOrientation 设置,也允许存在一些例外情况)。

旋转锁定模式的工作原理是:当设备旋转方向发生变化时,在导航栏中显示一个按钮。为此,即使自动屏幕旋转模式处于关闭状态,设备的屏幕方向传感器也必须一直保持启用状态。点按此按钮即相当于设定用户旋转方向偏好设置 (Settings.System.USER_ROTATION)。WindowManager 会根据此偏好设置以及有关顶层 activity 和系统状态的其他详细信息来改变系统的旋转方向。移动到另一个 Activity 时,为了确定以何种旋转方向呈现系统,WindowManager 会继续参考用户的旋转方向偏好设置。

在此 GIF 图片中,一部屏幕处于纵向方向的手机变为横向放置。此时出现一个图标,询问用户是否要将屏幕方向更改为横向。
图 1. 启用“在主屏幕按钮上向上滑动”手势后的旋转建议按钮

在多个 activity 之间移动时,应遵循用户旋转方向偏好设置。不过,由于大多数手机用户只希望在短时间内暂时处于横屏模式,因此我们加入了自然屏幕方向偏差机制。只要系统旋转方向变为设备的自然屏幕方向,用户旋转方向偏好设置就会重置为设备的自然屏幕方向。对于大多数手机而言,设备的自然屏幕方向是纵向 (0º)。当用户使用只能纵向显示的应用、锁定手机或返回到启动器工作区时,系统通常会重置用户旋转方向偏好设置。

在过去十年间,用户与屏幕旋转方式的互动没有发生太大变化。根据用户过去寻找旋转选项的表现和导航栏中的按钮位置,他们可能难以轻松发现此功能。因此,我们向旋转按钮添加了介绍模式,它会在旋转按钮出现时将其突出显示。这一行为仅发生在用户进行前几次按钮互动时,之后便会停用。

源代码

Android 9 中加入了对旋转建议的支持。大部分代码变更都包含在下列文件中。

  • services/.../server/policy/PhoneWindowManager.java
    • 使用 WindowOrientationListener 的输出的钩子(MyOrientationListener,负责监控传感器以确定设备是否已旋转方向)
    • 使 WindowOrientationListener 保持活跃状态(即使自动屏幕旋转模式已停用)(参见 needSensorRunningLp()
    • 在考虑用户旋转方向偏好设置、顶层 Activity screenOrientation 设置和系统状态的情况下,计算系统旋转方向(参见 rotationForOrientationLw()
    • 确定顶层 Activity 是否可以旋转到指定的旋转状态(参见 isRotationChoicePossible()
  • SystemUI/.../statusbar/phone/NavigationBarFragment
    • 确定导航栏按钮是否应在接收到来自 PhoneWindowManager 的旋转建议回调时显示(参见 onRotationProposal()
    • 确定何时隐藏旋转导航栏按钮(参见对 setRotateSuggestionButtonState(false) 的调用)
    • 处理按钮超时,包括导航栏处于隐藏状态时的特殊情况(通常在全屏模式下)
    • 在返回到设备的自然屏幕方向时重置用户偏好设置 (mRotationWatcher)
    • NavigationBarView 中采用的导航栏按钮动画选择适当样式(参见 onRotationProposal()
    • 添加了介绍模式逻辑,包括专门的动画(参见对 Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED 的引用)
    • 实现 disable2 旋转标志(参见 disable()
  • SystemUI/.../statusbar/phone/NavigationBarView.java
    • 设置按钮图标动画样式,使其与将要进行的旋转相匹配(参见 updateRotateSuggestionButtonStyle()
    • 处理按钮可见性更改(参见 setRotateButtonVisibility()),其中包括在某些无障碍服务处于有效状态时隐藏旋转按钮的逻辑(用来计算最右侧导航栏按钮堆栈排名)
  • SystemUI/res/layout/menu_ime.xml
    • 为旋转按钮添加新的 KeyButtonView,使其叠加在菜单和 IME/键盘选择器上方,但位于无障碍按钮的下方
  • SystemUI/res/drawable/ic_sysbar_rotate_button.xml
    • 复杂的 AnimatedVectorDrawable,用于为旋转导航栏按钮添加动画效果
    • 样式设置(位于 SystemUI/res/values/styles.xml 中)用于设置旋转的开始角度和结束角度,以便使用相同的可绘制对象,为各种开始和结束角度的旋转添加动画效果
    • 图标的色调调节通过 TintedKeyButtonDrawable 进行设置

实现

Android 9 包含了所有必要的更改,以获取使用软件导航键(返回、主屏幕等)的设备所适用的旋转建议。

如果设备制造商打造的设备采用的是硬件导航键且希望实现此功能,则需要设计和实现他们自己的系统界面或停用该功能。我们建议,当设备保持在与当前系统旋转方向呈 90º 或 180º 的角度时,引入的任何 Surface 都应可快速找到且易于使用。因此,不建议像为 IME/键盘选择器使用通知一样为该功能使用通知。

使用此功能的硬件要求与使用自动屏幕旋转的要求相同。

为确保实现的一致性,当自动屏幕旋转设置处于关闭状态时,如果系统出于任何原因将设备设置更改为自然旋转,用户旋转方向偏好设置 (Settings.System.USER_ROTATION) 需重置为设备的自然旋转方向。我们提供的实现就是这样做的(参见 NavigationBarFragment.mRotationWatcher)。

StatusBarManager.disable2 中有一个新标志,可暂时阻止系统显示旋转建议。请参见 StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS。所有实现都必须遵循此标志,因为关键系统应用(包括设置向导)需要用到此标志。我们提供的实现支持此标志(参见 NavigationBarFragment.disable())。

我们强烈建议您启用该功能并遵循 AOSP 实现(如果可能)。我们的目标是确保用户在使用不同的设备时可获得相似的屏幕旋转体验,从而体现出当今大多数手机上提供的自动屏幕旋转和纵向锁定的体验一致性。

自定义

由于旋转建议仅在旋转锁定模式(自动屏幕旋转处于关闭状态)下显示,因此您可以选择让自动屏幕旋转默认处于关闭状态,从而选择是否让该功能在新安装的系统中默认处于开启状态。请参阅 SettingsProvider/res/values/defaults.xml 中的 def_accelerometer_rotation,以进行默认设置更改。

用户可以通过“快捷设置”或“显示”设置中的旋转贴块,轻松更改自动屏幕旋转的启用状态(无论默认设置如何)。

验证

对于测试,您可以通过更改门槛 Settings.Secure 值来关闭和开启该功能。要实现这一点,最简单的方法是从特权 adb 实例运行以下命令:

adb shell settings put secure show_rotation_suggestions <x>

将 x 设为 0 表示关闭该功能;将其设为 1 表示开启该功能。

对于测试,您可以通过更改关联的 Settings.Secure 值来重置介绍模式。要实现这一点,最简单的方法是从特权 adb 实例运行以下命令:

adb shell settings put secure num_rotation_suggestions_accepted 0