Fix multi-activity pip flickers
The flicker on enter is a simple fix, the original pip task wasn't collected so it's surface hide wasn't part of the sync transaction. Just collect it to solve that. For disappearing in overview, the problem is more subtle: With shell-transitions, pip-entry happens at the end of the transient-launch transition. This means the pipping activity is starting off behind the home task. With multi-activity, a new task is created to host the pip, but it is placed on TOP, so when we reparent the pip activity into it, because it is going from visibleRequested=false to visReq=true, it triggers focus logic with in-turn triggers RecentTasks logic. Because it is a temporary task, RecentTasks gets confused and marks the pip's original task as hidden. This then triggers removal on idle which in-turn removes the pip activity since it's really part of the original task. To fix this, we set the windowing-mode of the new (temporary) task to PINNED *before* reparenting the activity into it. That way it doesn't try to grab focus. Also, to make things clearer, this re-arranges some things: multi-activity pip-task doesn't have fragments, so moved the fragment code into the singleActivity case so its more obvious that this is the case. Did the same thing with "reparent to activity TDA" because the new task is created there already. Also made the temporary task match the original task's override windowing mode (instead of UNDEFINED), otherwise it can create flickers when pipping from a task in a different mode from the display. Bug: 269910373 Test: open multi-activity pip app, then swipe to home. Change-Id: I9e1b36b47e7de331db2fcea3d1b1b6cfd2d34a26
This commit is contained in:
@@ -1997,6 +1997,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
// of the activity entering PIP
|
||||
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
||||
|
||||
transitionController.collect(task);
|
||||
|
||||
// Defer the windowing mode change until after the transition to prevent the activity
|
||||
// from doing work and changing the activity visuals while animating
|
||||
// TODO(task-org): Figure-out more structured way to do this long term.
|
||||
r.setWindowingMode(r.getWindowingMode());
|
||||
r.mWaitForEnteringPinnedMode = true;
|
||||
|
||||
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
|
||||
final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
|
||||
if (singleActivity) {
|
||||
@@ -2004,6 +2012,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
|
||||
// Apply the last recents animation leash transform to the task entering PIP
|
||||
rootTask.maybeApplyLastRecentsAnimationTransaction();
|
||||
|
||||
if (rootTask.getParent() != taskDisplayArea) {
|
||||
// root task is nested, but pinned tasks need to be direct children of their
|
||||
// display area, so reparent.
|
||||
rootTask.reparent(taskDisplayArea, true /* onTop */);
|
||||
}
|
||||
|
||||
rootTask.forAllTaskFragments(tf -> {
|
||||
if (!tf.isOrganizedTaskFragment()) {
|
||||
return;
|
||||
}
|
||||
tf.resetAdjacentTaskFragment();
|
||||
tf.setCompanionTaskFragment(null /* companionTaskFragment */);
|
||||
tf.setAnimationParams(TaskFragmentAnimationParams.DEFAULT);
|
||||
if (tf.getTopNonFinishingActivity() != null) {
|
||||
// When the Task is entering picture-in-picture, we should clear all
|
||||
// override from the client organizer, so the PIP activity can get the
|
||||
// correct config from the Task, and prevent conflict with the
|
||||
// PipTaskOrganizer. TaskFragmentOrganizer may have requested relative
|
||||
// bounds, so reset the relative bounds before update configuration.
|
||||
tf.setRelativeEmbeddedBounds(new Rect());
|
||||
tf.updateRequestedOverrideConfiguration(EMPTY);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// In the case of multiple activities, we will create a new task for it and then
|
||||
// move the PIP activity into the task. Note that we explicitly defer the task
|
||||
@@ -2016,6 +2048,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
.setIntent(r.intent)
|
||||
.setDeferTaskAppear(true)
|
||||
.setHasBeenVisible(true)
|
||||
.setWindowingMode(task.getRequestedOverrideWindowingMode())
|
||||
.build();
|
||||
// Establish bi-directional link between the original and pinned task.
|
||||
r.setLastParentBeforePip(launchIntoPipHostActivity);
|
||||
@@ -2049,6 +2082,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
organizedTf.mClearedTaskFragmentForPip = true;
|
||||
}
|
||||
|
||||
transitionController.collect(rootTask);
|
||||
|
||||
if (transitionController.isShellTransitionsEnabled()) {
|
||||
// set mode NOW so that when we reparent the activity, it won't be resumed.
|
||||
// During recents animations, the original task is "occluded" by launcher but
|
||||
// it wasn't paused (due to transient-launch). If we reparent to the (top) task
|
||||
// now, it will take focus briefly which confuses the RecentTasks tracker.
|
||||
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
}
|
||||
|
||||
// There are multiple activities in the task and moving the top activity should
|
||||
// reveal/leave the other activities in their original task.
|
||||
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
|
||||
@@ -2073,40 +2116,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
}
|
||||
}
|
||||
|
||||
transitionController.collect(rootTask);
|
||||
|
||||
// The intermediate windowing mode to be set on the ActivityRecord later.
|
||||
// This needs to happen before the re-parenting, otherwise we will always set the
|
||||
// ActivityRecord to be fullscreen.
|
||||
final int intermediateWindowingMode = rootTask.getWindowingMode();
|
||||
if (rootTask.getParent() != taskDisplayArea) {
|
||||
// root task is nested, but pinned tasks need to be direct children of their
|
||||
// display area, so reparent.
|
||||
rootTask.reparent(taskDisplayArea, true /* onTop */);
|
||||
}
|
||||
|
||||
// Defer the windowing mode change until after the transition to prevent the activity
|
||||
// from doing work and changing the activity visuals while animating
|
||||
// TODO(task-org): Figure-out more structured way to do this long term.
|
||||
r.setWindowingMode(intermediateWindowingMode);
|
||||
r.mWaitForEnteringPinnedMode = true;
|
||||
rootTask.forAllTaskFragments(tf -> {
|
||||
if (!tf.isOrganizedTaskFragment()) {
|
||||
return;
|
||||
}
|
||||
tf.resetAdjacentTaskFragment();
|
||||
tf.setCompanionTaskFragment(null /* companionTaskFragment */);
|
||||
tf.setAnimationParams(TaskFragmentAnimationParams.DEFAULT);
|
||||
if (tf.getTopNonFinishingActivity() != null) {
|
||||
// When the Task is entering picture-in-picture, we should clear all override
|
||||
// from the client organizer, so the PIP activity can get the correct config
|
||||
// from the Task, and prevent conflict with the PipTaskOrganizer.
|
||||
// TaskFragmentOrganizer may have requested relative bounds, so reset the
|
||||
// relative bounds before update configuration.
|
||||
tf.setRelativeEmbeddedBounds(new Rect());
|
||||
tf.updateRequestedOverrideConfiguration(EMPTY);
|
||||
}
|
||||
});
|
||||
// TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing
|
||||
// legacy transit.
|
||||
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
// Set the launch bounds for launch-into-pip Activity on the root task.
|
||||
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
|
||||
|
||||
Reference in New Issue
Block a user