Android 13 PIP画中画模式系统流程
2023-02-19 本文已影响0人
孤街酒客0911
学习笔记:
一、画中画创建
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);
}
}