1、概述流程
创建pip stack → 创建新的TaskRecord → activity reparent到newTask → newTask reparent到pip stack → resumeFocusedStacksTopActivitive
2、时序图
3、相关代码解析
app端binder回调到ATMS的方法,如果有keyguard,先dismiss keyguard再进入pip
// ActivityTaskManagerService
@Override
public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
"enterPictureInPictureMode", token, params);
// If the activity is already in picture in picture mode, then just return early
if (isInPictureInPictureMode(r)) {
return true;
}
// Activity supports picture-in-picture, now check that we can enter PiP at this
// point, if it is
// 是否能进入pip的一些判断处理,activity是否配置,keyguard、activity state等
if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
false /* beforeStopping */)) {
return false;
}
final Runnable enterPipRunnable = () -> {
synchronized (mGlobalLock) {
if (r.finishing || r.getTaskRecord() == null) return;
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(params);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List actions = r.pictureInPictureArgs.getActions();
// Adjust the source bounds by the insets for the transition down
final Rect sourceBounds = new Rect(
r.pictureInPictureArgs.getSourceRectHint());
mRootActivityContainer.moveActivityToPinnedStack(
r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
final ActivityStack stack = r.getActivityStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
logPictureInPictureArgs(params);
}
};
// keyguard显示,先dismiss keyguard再进入pip,否则直接进入pip
if (isKeyguardLocked()) {
// If the keyguard is showing or occluded, then try and dismiss it before
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
mH.post(enterPipRunnable);
}
}, null /* message */);
} else {
// Enter picture in picture immediately otherwise
enterPipRunnable.run();
}
return true;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
// task stack 动画等相关处理
// RootActivityContainer
void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
String reason) {
mWindowManager.deferSurfaceLayout();
final ActivityDisplay display = r.getActivityStack().getDisplay();
// 一般情况下获取都为null,创建pip stack时会赋值,见如下:2-1)
ActivityStack stack = display.getPinnedStack();
// This will clear the pinned stack by moving an existing task to the full screen stack,
// ensuring only one task is present.
if (stack != null) {
mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP);
}
// Need to make sure the pinned stack exist so we can resize it below...
stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
// Calculate the target bounds here before the task is reparented back into pinned windowing
// mode (which will reset the saved bounds)
// Rect(508, 1869 - 1036, 2166)
// 计算pip stack边界
final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
try {
final TaskRecord task = r.getTaskRecord();
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
// 没看到有太大作用
resizeStack(stack, task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
// 一般task的mActivities大小会大于1,因为会有主页,再是视频详情页
if (task.mActivities.size() == 1) {
// Defer resume until below, and do not schedule PiP changes until we animate below
task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
false /* schedulePictureInPictureModeChange */, reason);
} else {
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
// Currently, we don't support reparenting activities across tasks in two different
// stacks, so instead, just create a new task in the same stack, reparent the
// activity into that task, and then reparent the whole task to the new stack. This
// ensures that all the necessary work to migrate states in the old and new stacks
// is also done.
// 在原有stack上新建一个task
final TaskRecord newTask = task.getStack().createTaskRecord(
mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info,
r.intent, null, null, true);
// Activity reparent到新建的task
r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
// Defer resume until below, and do not schedule PiP changes until we animate below
// task reparent到pip stack
newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
}
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
} finally {
mWindowManager.continueSurfaceLayout();
}
stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
true /* fromFullscreen */);
// Update the visibility of all activities after the they have been reparented to the new
// stack. This MUST run after the animation above is scheduled to ensure that the windows
// drawn signal is scheduled after the bounds animation start call on the bounds animator
// thread.
// 显示
ensureActivitiesVisible(null, 0, false /* preserveWindows */);
resumeFocusedStacksTopActivities();
mService.getTaskChangeNotificationController().notifyActivityPinned(r);
}
// 根据fromFullscreen判断进入还是退出pip,执行相关动画
// TaskStack
/** Do not schedule any PiP mode changed callbacks as a part of this animation. */
public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
/** Schedule a PiP mode changed callback when this animation starts. */
public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
/** Schedule a PiP mode changed callback when this animation ends. */
public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
int animationDuration, boolean fromFullscreen) {
if (!inPinnedWindowingMode()) {
return;
}
// Get non-null fullscreen to-bounds for animating if the bounds are null
@SchedulePipModeChangedState int schedulePipModeChangedState =
NO_PIP_MODE_CHANGED_CALLBACKS;
final boolean toFullscreen = toBounds == null;
// 退出pip
if (toFullscreen) {
if (fromFullscreen) {
throw new IllegalArgumentException("Should not defer scheduling PiP mode"
+ " change on animation to fullscreen.");
}
schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
mWmService.getStackBounds(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
if (!mTmpToBounds.isEmpty()) {
// If there is a fullscreen bounds, use that
toBounds = new Rect(mTmpToBounds);
} else {
// Otherwise, use the display bounds
toBounds = new Rect();
getDisplayContent().getBounds(toBounds);
}
// 进入pip
} else if (fromFullscreen) {
schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
}
// 设置动画的边界Rect(0, 0 - 1080, 2340) 到 Rect(508, 1869 - 1036, 2166)
setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
final Rect finalToBounds = toBounds;
final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
schedulePipModeChangedState;
final DisplayContent displayContent = getDisplayContent();
@BoundsAnimationController.AnimationType int intendedAnimationType =
displayContent.mBoundsAnimationController.getAnimationType();
if (intendedAnimationType == FADE_IN) {
if (fromFullscreen) {
setPinnedStackAlpha(0f);
}
if (toBounds.width() == fromBounds.width()
&& toBounds.height() == fromBounds.height()) {
intendedAnimationType = BoundsAnimationController.BOUNDS;
}
}
final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
mCancelCurrentBoundsAnimation = false;
displayContent.mBoundsAnimationController.getHandler().post(() -> {
displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
finalToBounds, animationDuration, finalSchedulePipModeChangedState,
fromFullscreen, toFullscreen, animationType);
});
}
1、概述流程
执行退出动画 → 把对应的task 移到全屏的stack → resume stack的activiy
相关log
02-18 16:56:22.212 1295 1315 I am_remove_task: [27,13]
02-18 16:56:22.219 1295 1315 I am_focused_stack: [0,0,14,0,moveTasksToFullscreenStack - onTop]
// ActivityStackSupervisor
/**
* TODO: This should just change the windowing mode and resize vs. actually moving task around.
* Can do that once we are no longer using static stack ids.
*/
private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
int toDisplayId, boolean onTop) {
mWindowManager.deferSurfaceLayout();
try {
final int windowingMode = fromStack.getWindowingMode();
final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
final ActivityDisplay toDisplay =
mRootActivityContainer.getActivityDisplay(toDisplayId);
......
// If we are moving from the pinned stack, then the animation takes care of updating
// the picture-in-picture mode.
final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
final ArrayList tasks = fromStack.getAllTasks();
if (!tasks.isEmpty()) {
mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int size = tasks.size();
for (int i = 0; i < size; ++i) {
final TaskRecord task = tasks.get(i);
final ActivityStack toStack = toDisplay.getOrCreateStack(
null, mTmpOptions, task, task.getActivityType(), onTop);
if (onTop) {
final boolean isTopTask = i == (size - 1);
// Defer resume until all the tasks have been moved to the fullscreen stack
task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
isTopTask /* animate */, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - onTop");
MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
task.effectiveUid, task.realActivity.flattenToString());
} else {
// Position the tasks in the fullscreen stack in order at the bottom of the
// stack. Also defer resume until all the tasks have been moved to the
// fullscreen stack.
task.reparent(toStack, ON_TOP,
REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - NOT_onTop");
}
}
}
mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mRootActivityContainer.resumeFocusedStacksTopActivities();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
}
}