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

Commit 36b7fb7f authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Respect hierarchy visibility of transient launch

This fixes two cases:
1. When swiping to home with pip or freeform on top, the transient
   launch (home or recent) is misjudged as occluded because the
   always-on-top task is also opaque.
2. After swiping to another app, even if recent is occluded by
   the new top, the recent is still considered as visible before
   the transition is finished.

Bug: 362033992
Flag: EXEMPT bugfix
Test: TransitionTests#testIsTransientVisible
Change-Id: I52bb8ecbed7437107c776ad5fdd4b4d4d4d4f962
parent 38c9e553
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -2894,10 +2894,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        private boolean mIncludeInvisibleAndFinishing;
        private boolean mIgnoringKeyguard;

        ActivityRecord getOpaqueActivity(
                @NonNull WindowContainer<?> container, boolean ignoringKeyguard) {
        ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) {
            mIncludeInvisibleAndFinishing = true;
            mIgnoringKeyguard = ignoringKeyguard;
            mIgnoringKeyguard = true;
            return container.getActivity(this,
                    true /* traverseTopToBottom */, null /* boundary */);
        }
+7 −2
Original line number Diff line number Diff line
@@ -1090,8 +1090,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
            return true;
        }
        // Including finishing Activity if the TaskFragment is becoming invisible in the transition.
        return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this,
                true /* ignoringKeyguard */) == null;
        return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null;
    }

    /**
@@ -1734,6 +1733,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        if (!hasDirectChildActivities()) {
            return false;
        }
        if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) {
            // Even if the transient activity is occluded, defer pausing (addToStopping will still
            // be called) it until the transient transition is done. So the current resuming
            // activity won't need to wait for additional pause complete.
            return false;
        }

        ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
                mResumedActivity);
+8 −11
Original line number Diff line number Diff line
@@ -469,20 +469,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
            if (transientRoot == null) continue;
            final WindowContainer<?> rootParent = transientRoot.getParent();
            if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
            final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor.mOpaqueActivityHelper
                    .getOpaqueActivity(rootParent, true /* ignoringKeyguard */);
            if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
            for (int j = rootParent.getChildCount() - 1; j >= 0; --j) {
                final WindowContainer<?> sibling = rootParent.getChildAt(j);
                if (sibling == transientRoot) break;
                if (!sibling.getWindowConfiguration().isAlwaysOnTop() && mController.mAtm
                        .mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(sibling) != null) {
                    occludedCount++;
                    break;
                }
            }
        if (occludedCount == numTransient) {
            for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
                if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
                    // Keep transient activity visible until transition finished, so it won't pause
                    // with transient-hide tasks that may delay resuming the next top.
                    return true;
                }
        }
        if (occludedCount == numTransient) {
            // Let transient-hide activities pause before transition is finished.
            return false;
        }
+13 −5
Original line number Diff line number Diff line
@@ -1632,6 +1632,8 @@ public class TransitionTests extends WindowTestsBase {
        transition.collect(taskA);
        transition.setTransientLaunch(recent, taskA);
        taskRecent.moveToFront("move-recent-to-front");
        recent.setVisibility(true);
        recent.setState(ActivityRecord.State.RESUMED, "test");

        // During collecting and playing, the recent is on top so it is visible naturally.
        // While B needs isTransientVisible to keep visibility because it is occluded by recents.
@@ -1644,15 +1646,21 @@ public class TransitionTests extends WindowTestsBase {

        // Switch to another task. For example, use gesture navigation to switch tasks.
        taskB.moveToFront("move-b-to-front");
        appB.setVisibility(true);
        // The previous app (taskA) should be paused first so it loses transient visible. Because
        // visually it is taskA -> taskB, the pause -> resume order should be the same.
        assertFalse(controller.isTransientVisible(taskA));
        // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
        // to avoid the latency to resume the current top, i.e. appB.
        assertTrue(controller.isTransientVisible(taskRecent));
        // The recent is paused after the transient transition is finished.
        controller.finishTransition(ActionChain.testFinish(transition));
        // The recent is occluded by appB.
        assertFalse(controller.isTransientVisible(taskRecent));
        // Active transient launch won't be paused if the transition is not finished. It is to
        // avoid the latency to resume the current top (appB) by waiting for both recent and appA
        // to complete pause.
        assertEquals(recent, taskRecent.getResumedActivity());
        assertFalse(taskRecent.startPausing(false /* uiSleeping */, appB /* resuming */, "test"));
        // ActivityRecord#makeInvisible will add the invisible recent to the stopping list.
        // So when the transition finished, the recent can still be notified to pause and stop.
        mDisplayContent.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
        assertTrue(mSupervisor.mStoppingActivities.contains(recent));
    }

    @Test