画中画

借助 Android 手持设备的画中画 (PIP) 功能,用户可以将当前正在运行 activity 的应用缩到小窗口中显示。画中画对视频应用来说尤其有用,因为用户可以随时执行其他操作,不必打断内容播放。用户可以通过 SystemUI 操控该窗口的位置,并通过应用提供的操作(最多三项)与当前处于画中画模式的应用流畅地互动。

画中画功能需要在支持它的应用中明确选择启用,并按 activity 运作(一个应用可以有多个 activity,但其中只有一个可处于画中画模式)。Activity 通过调用 enterPictureInPictureMode() 请求以进入画中画模式,并以 onPictureInPictureModeChanged() 的形式接收 Activity 回调。

setPictureInPictureParams() 方法可让 activity 控制其在画中画模式下的宽高比和自定义操作,这样一来,用户无需展开 activity 便可以与之互动。在画中画模式下,activity 处于暂停但继续呈现的状态,并且不直接接收触摸输入或窗口焦点。在同一时间点,只能有一项任务处于画中画模式。

如需了解详情,请参阅 Android 开发者画中画文档。

设备要求

要支持画中画功能,请在 /android/frameworks/base/core/java/android/content/pm/PackageManager.java 中启用 PackageManager#FEATURE_PICTURE_IN_PICTURE 系统功能。支持画中画功能的设备的最小屏幕宽度必须大于 220dp。与分屏多窗口类似,画中画功能支持多个 activity 同时在屏幕上运行。因此,设备的 CPU 和 RAM 要足以支持这类使用情形。

实现

大多数 activity 生命周期管理都发生在 ActivityManagerWindowManager 系统中。参考界面实现位于 SystemUI 软件包中。

对系统所做的修改不应影响其内在行为(如兼容性测试套件 (CTS) 测试中所定义)。画中画功能的系统逻辑主要围绕“置顶”堆栈中的任务和 activity 管理而展开。以下是对系统逻辑类的简单介绍:

  • ActivityRecord:跟踪每个 activity 的画中画状态。为了防止用户在某些情况下(例如从锁定屏幕或在 VR 中)进入画中画,请向 checkEnterPictureInPictureState() 添加相应用例。
  • ActivityManagerService:activity 在请求进入画中画模式时所调用的主接口,也是 WindowManagerSystemUI 更改画中画 activity 状态时所调用的接口。
  • ActivityStackSupervisor:从 ActivityManagerService 调用以将任务移入或移出固定的堆栈,并根据需要更新 WindowManager
  • PinnedStackWindowController:来自 ActivityManagerWindowManager 接口。
  • PinnedStackController:将系统中的更改报告给 SystemUI,例如 IME 显示/隐藏、宽高比已更改或操作发生变化。
  • BoundsAnimationController:以不触发配置更改(在调整大小的过程中)的方式,对画中画 Activity 窗口进行动画处理。
  • PipSnapAlgorithm:系统和 SystemUI 中使用的共享类,可控制屏幕边缘附近的画中画窗口的贴靠行为。

参考 SystemUI 提供画中画功能的完整实现(支持向用户呈现自定义操作并执行展开和关闭等常规操作)。设备制造商可以在这些变更的基础上进行构建,只要它们不影响 CDD 定义的内在行为即可。以下是对系统逻辑类的简单介绍:

  • PipManager:以 SystemUI 开头的 SystemUI 组件。
  • PipTouchHandler:触摸处理程序,用于控制操控画中画的手势。只有在画中画的处于活动状态的用户处于此状态时,才使用此属性(请参阅 InputConsumerController)。可在此添加新手势。
  • PipMotionHelper:一个辅助类,用于跟踪画中画的位置和屏幕上允许的区域。通过调用 ActivityManagerService 来更新画中画,或调整画中画的位置和大小。
  • PipMenuActivityController:启动一个 Activity,以显示当前画中画中的 Activity 提供的操作。此 activity 是任务叠加层 activity,并移除叠加输入使用方,使其处于互动状态。
  • PipMenuActivity:菜单 Activity 的实现。
  • PipMediaController:当媒体会话以可能影响画中画的默认操作的方式发生变化时更新 SystemUI 的监听器。
  • PipNotificationController:确保在用户使用画中画功能时显示一条通知的控制器。
  • PipDismissViewController:当用户开始与画中画互动时向用户显示的叠加层,以表明用户可以关闭画中画。

默认显示位置

用于控制画中画的默认显示位置的系统资源有多项:

  • config_defaultPictureInPictureGravity重力整数,用于控制放置画中画的角落,例如 BOTTOM|RIGHT
  • config_defaultPictureInPictureScreenEdgeInsets:放置画中画的位置与屏幕侧边的偏移量。
  • config_pictureInPictureDefaultSizePercentconfig_pictureInPictureDefaultAspectRatio:屏幕宽度的百分比和宽高比的组合控制画中画的大小。根据 CTS 和 CDD 的定义,计算的默认画中画大小不得小于 @dimen/default_minimal_size_pip_resizable_task
  • config_pictureInPictureSnapModePipSnapAlgorithm 中定义的贴靠行为。

设备实现不应更改 CDD 和 CTS 中定义的宽高比上限和下限。

权限

AppOpsManager (main/core/java/android/app/AppOpsManager.java) 中的基于软件包的“应用操作”(OP_PICTURE_IN_PICTURE),可让用户通过系统设置在应用级别控制画中画。当 activity 请求进入画中画模式时,设备实现需要遵循此检查要求。

测试

如需测试画中画实现,请在 /cts/hostsidetests/services/activitymanager 下的主机端 CTS 测试中找到所有与画中画相关的测试,尤其是 ActivityManagerPinnedStackTests.java