画中画

借助 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 来请求进入 PIP,以及从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:当媒体会话以可能影响 PIP 的默认操作的方式发生变化时更新 SystemUI 的监听器。
  • PipNotificationController:确保在用户使用画中画功能时显示一条通知的控制器。
  • PipDismissViewController:当用户开始与画中画互动时向用户显示的叠加层,以表明用户可以关闭画中画。

默认显示位置

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

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

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

权限

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

测试

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