一、画中画创建
Activity调用系统api:ActivityTaskManagerService#enterPictureInPictureMode() 开始:
// ActivityTaskManagerService.java boolean enterPictureInPictureMode(@NonNull ActivityRecord r, PictureInPictureParams params) { // 如果 activity 已经处于画中画模式,则返回 if (r.inPinnedWindowingMode()) { return true; } // Activity支持画中画,现在检查我们此时是否可以进入画中画, if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", false /* beforeStopping */)) { return false; } // 创建了一个 Runnable,在下方将会被调用 final Runnable enterPipRunnable = () -> { synchronized (mGlobalLock) { if (r.getParent() == null) { Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); return; } r.setPictureInPictureParams(params); // **--------重点关注-------------** mRootWindowContainer.moveActivityToPinnedRootTask(r, null /* launchIntoPipHostActivity */, "enterPictureInPictureMode"); final Task task = r.getTask(); // Continue the pausing process after entering pip. if (task.getPausingActivity() == r) { task.schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, "auto-pip"); } } }; if (r.isKeyguardLocked()) { // 如果键盘锁正在显示或被遮挡,则在进入画中画之前尝试关闭它 // 如果设备当前已锁定(有锁的情况),这将提示用户进行身份验证(即弹出bouncer)。 mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() { @Override public void onDismissSucceeded() { mH.post(enterPipRunnable); } }, null /* message */); } else { // 否则立即进入画中画 enterPipRunnable.run(); } return true; }
如果活动现在处于画中画模式,则为 return true;如果无法进入画中画模式,则为 return false。
App 端 binder 回调到 ATMS 的方法,如果有 keyguard,先 dismiss keyguard 再进入pip。
接着根据上述代码看 RootWindowContainer#moveActivityToPinnedRootTask():
// RootWindowContainer.java void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason) { mService.deferWindowLayout(); final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); try { // 获取任务栈 final Task task = r.getTask(); // 现在创建一个转换控制器以收集当前关闭的任务栈。 // 由于进入 PIP 的任务(触发器)尚未准备好,因此在此处进行创建。 final TransitionController transitionController = task.mTransitionController; Transition newTransition = null; if (transitionController.isCollecting()) { transitionController.setReady(task, false /* ready */); } else if (transitionController.getTransitionPlayer() != null) { newTransition = transitionController.createTransition(TRANSIT_PIP); } final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); if (rootPinnedTask != null) { transitionController.collect(rootPinnedTask); // 新的ActivityRecord应该取代现有的PiP,因此旧的PiP消失而不是同时转到全屏, // 正如Task#diseasePip试图做的那样。 removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); } // 设置过渡以确保我们不会立即尝试更新可见性,进入 PIP 的活动 r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); final TaskFragment organizedTf = r.getOrganizedTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; final Task rootTask; if (singleActivity) { rootTask = task; // 对进入PIP的任务应用最近一次的动画牵引变换 rootTask.maybeApplyLastRecentsAnimationTransaction(); } else { // 在多个活动的情况下,我们将为其创建一个新任务, // 然后将PIP活动移动到任务中。 // 注意,我们明确地延迟了正在发送的任务,并将这个新创建的任务标记为可见 rootTask = new Task.Builder(mService) .setActivityType(r.getActivityType()) .setOnTop(true) .setActivityInfo(r.info) .setParent(taskDisplayArea) .setIntent(r.intent) .setDeferTaskAppear(true) .setHasBeenVisible(true) .build(); // 在原始任务和固定任务之间建立双向链接。 r.setLastParentBeforePip(launchIntoPipHostActivity); // 进入 PIP 的任务可能是自由格式的,因此请保存最后一个非全屏边界。 // 然后当这个新的 PIP 任务退出 PIP 时,它可以恢复到它以前的自由形式边界 rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds); rootTask.setBounds(task.getBounds()); // 将最后一个最近的动画事务从原始任务移动到新任务 if (task.mLastRecentsAnimationTransaction != null) { rootTask.setLastRecentsAnimationTransaction( task.mLastRecentsAnimationTransaction, task.mLastRecentsAnimationOverlay); task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */); } if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1 && organizedTf.getTopNonFinishingActivity() == r) { // PIP 的状态,是否已清除 PIP 的TaskFragment。 // 一旦有改变将在 ActivityTaskManagerService#onPictureInPictureStateChanged() 收到回调 organizedTf.mClearedTaskFragmentForPip = true; } // Activity reparent 到新建的 task r.reparent(rootTask, MAX_VALUE, reason); // 确保修复后新任务的约束与其当前边界同步。 rootTask.maybeApplyLastRecentsAnimationTransaction(); final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.containsTransitRequest( TRANSIT_TO_BACK)) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } } final int intermediateWindowingMode = rootTask.getWindowingMode(); if (rootTask.getParent() != taskDisplayArea) { rootTask.reparent(taskDisplayArea, true /* onTop */); } if (newTransition != null) { transitionController.requestStartTransition(newTransition, rootTask, null /* remoteTransition */, null /* displayChange */); } transitionController.collect(rootTask); r.setWindowingMode(intermediateWindowingMode); r.mWaitForEnteringPinnedMode = true; rootTask.forAllTaskFragments(tf -> { if (!tf.isOrganizedTaskFragment()) { return; } tf.resetAdjacentTaskFragment(); if (tf.getTopNonFinishingActivity() != null) { tf.updateRequestedOverrideConfiguration(EMPTY); } }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { mWindowManager.mTaskSnapshotController.recordTaskSnapshot( task, false /* allowSnapshotHome */); rootTask.setBounds(r.getOptions().getLaunchBounds()); } rootTask.setDeferTaskAppear(false); r.supportsEnterPipOnTaskSwitch = false; if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip && organizedTf.isTaskVisibleRequested()) { mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent( organizedTf); } } finally { mService.continueWindowLayout(); } // 显示 ensureActivitiesVisible(null, 0, false /* preserveWindows */); resumeFocusedTasksTopActivities(); notifyActivityPipModeChanged(r.getTask(), r); }
二、退出PIP模式
当 PIP 状态改变,将会在 ActivityTaskManagerService#onPictureInPictureStateChanged() 收到回调:
// ActivityTaskManagerService.java @Override public void onPictureInPictureStateChanged(PictureInPictureUiState pipState) { enforceTaskPermission("onPictureInPictureStateChanged"); final Task rootPinnedTask = mRootWindowContainer.getDefaultTaskDisplayArea() .getRootPinnedTask(); if (rootPinnedTask != null && rootPinnedTask.getTopMostActivity() != null) { // 这里将会去提醒客户端状态发生改变; mWindowManager.mAtmService.mActivityClientController.onPictureInPictureStateChanged( rootPinnedTask.getTopMostActivity(), pipState); } }
ActivityClientController#onPictureInPictureStateChanged():
void onPictureInPictureStateChanged(@NonNull ActivityRecord r, PictureInPictureUiState pipState) { if (!r.inPinnedWindowingMode()) { throw new IllegalStateException("Activity is not in PIP mode"); } try { final ClientTransaction transaction = ClientTransaction.obtain( r.app.getThread(), r.token); transaction.addCallback(PipStateTransactionItem.obtain(pipState)); mService.getLifecycleManager().scheduleTransaction(transaction); } catch (Exception e) { Slog.w(TAG, "Failed to send pip state transaction item: " + r.intent.getComponent(), e); } }