Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fdfb944f authored by Evan Rosky's avatar Evan Rosky
Browse files

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
parent 62190cc2
Loading
Loading
Loading
Loading
+45 −34
Original line number Original line Diff line number Diff line
@@ -1997,6 +1997,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
            // of the activity entering PIP
            // of the activity entering PIP
            r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
            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 TaskFragment organizedTf = r.getOrganizedTaskFragment();
            final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
            final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
            if (singleActivity) {
            if (singleActivity) {
@@ -2004,6 +2012,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent>


                // Apply the last recents animation leash transform to the task entering PIP
                // Apply the last recents animation leash transform to the task entering PIP
                rootTask.maybeApplyLastRecentsAnimationTransaction();
                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 {
            } else {
                // In the case of multiple activities, we will create a new task for it and then
                // 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
                // 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)
                        .setIntent(r.intent)
                        .setDeferTaskAppear(true)
                        .setDeferTaskAppear(true)
                        .setHasBeenVisible(true)
                        .setHasBeenVisible(true)
                        .setWindowingMode(task.getRequestedOverrideWindowingMode())
                        .build();
                        .build();
                // Establish bi-directional link between the original and pinned task.
                // Establish bi-directional link between the original and pinned task.
                r.setLastParentBeforePip(launchIntoPipHostActivity);
                r.setLastParentBeforePip(launchIntoPipHostActivity);
@@ -2049,6 +2082,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
                    organizedTf.mClearedTaskFragmentForPip = true;
                    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
                // There are multiple activities in the task and moving the top activity should
                // reveal/leave the other activities in their original task.
                // reveal/leave the other activities in their original task.
                // On the other hand, ActivityRecord#onParentChanged takes care of setting the
                // On the other hand, ActivityRecord#onParentChanged takes care of setting the
@@ -2073,40 +2116,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
                }
                }
            }
            }


            transitionController.collect(rootTask);
            // TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing

            //                              legacy transit.
            // 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);
                }
            });
            rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
            rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
            // Set the launch bounds for launch-into-pip Activity on the root task.
            // Set the launch bounds for launch-into-pip Activity on the root task.
            if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
            if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {