画中画

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

有关详情,请参阅 Android 开发者画中画文档。

概览

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

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

设备要求

要支持画中画功能,请在 /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_defaultPictureInPictureGravitygravity 整数,可控制放置画中画的角落,如 BOTTOM|RIGHT
  • config_defaultPictureInPictureScreenEdgeInsets:画中画的放置位置相对于屏幕侧边的偏移量。
  • config_pictureInPictureDefaultSizePercentconfig_pictureInPictureDefaultAspectRatio:占屏幕宽度的百分比与宽高比的组合,用于控制画中画的大小。计算得出的默认画中画大小不应小于 @dimen/default_minimal_size_pip_resizable_task(如 CTS 和 CDD 中所定义)。
  • config_pictureInPictureSnapMode:贴靠行为(如 PipSnapAlgorithm 中所定义)。

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

权限

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

测试

要测试画中画实现,请运行主机端 CTS 测试中 /cts/hostsidetests/services/activitymanager(尤其是 ActivityManagerPinnedStackTests.java 中)下所有与画中画相关的测试。