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

Commit b0af6d94 authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Keep visible requested for transient hide activities" into udc-dev

parents 38b109a4 211e1dd8
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -1680,6 +1680,11 @@ class ActivityStarter {
                targetTask.removeImmediately("bulky-task");
                return START_ABORTED;
            }
            // When running transient transition, the transient launch target should keep on top.
            // So disallow the transient hide activity to move itself to front, e.g. trampoline.
            if (!mAvoidMoveToFront && r.mTransitionController.isTransientHide(targetTask)) {
                mAvoidMoveToFront = true;
            }
            mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask());
        }

@@ -1796,7 +1801,7 @@ class ActivityStarter {
                // root-task to the will not update the focused root-task.  If starting the new
                // activity now allows the task root-task to be focusable, then ensure that we
                // now update the focused root-task accordingly.
                if (mTargetRootTask.isTopActivityFocusable()
                if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()
                        && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
                    mTargetRootTask.moveToFront("startActivityInner");
                }
+4 −0
Original line number Diff line number Diff line
@@ -1012,6 +1012,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        if (isTopActivityLaunchedBehind()) {
            return TASK_FRAGMENT_VISIBILITY_VISIBLE;
        }
        final Task thisTask = asTask();
        if (thisTask != null && mTransitionController.isTransientHide(thisTask)) {
            return TASK_FRAGMENT_VISIBILITY_VISIBLE;
        }

        boolean gotTranslucentFullscreen = false;
        boolean gotTranslucentAdjacent = false;
+54 −24
Original line number Diff line number Diff line
@@ -194,6 +194,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
     */
    private ArrayMap<ActivityRecord, Task> mTransientLaunches = null;

    /**
     * The tasks that may be occluded by the transient activity. Assume the task stack is
     * [Home, A(opaque), B(opaque), C(translucent)] (bottom to top), then A is the restore-below
     * task, and [B, C] are the transient-hide tasks.
     */
    private ArrayList<Task> mTransientHideTasks;

    /** Custom activity-level animation options and callbacks. */
    private TransitionInfo.AnimationOptions mOverrideOptions;
    private IRemoteCallback mClientAnimationStartCallback = null;
@@ -265,35 +272,51 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
    void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
        if (mTransientLaunches == null) {
            mTransientLaunches = new ArrayMap<>();
            mTransientHideTasks = new ArrayList<>();
        }
        mTransientLaunches.put(activity, restoreBelow);
        setTransientLaunchToChanges(activity);

        if (restoreBelow != null) {
            final ChangeInfo info = mChanges.get(restoreBelow);
            if (info != null) {
                info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
            // Collect all visible activities which can be occluded by the transient activity to
            // make sure they are in the participants so their visibilities can be updated when
            // finishing transition.
            ((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> {
                if (t.isVisibleRequested() && !t.isAlwaysOnTop()
                        && !t.getWindowConfiguration().tasksAreFloating()) {
                    if (t.isRootTask()) {
                        mTransientHideTasks.add(t);
                    }
                    if (t.isLeafTask()) {
                        t.forAllActivities(r -> {
                            if (r.isVisibleRequested()) {
                                collect(r);
                            }
                        });
                    }
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
                + "transient-launch", mSyncId, activity);
                }

    boolean isTransientHide(@NonNull Task task) {
        if (mTransientLaunches == null) return false;
        for (int i = 0; i < mTransientLaunches.size(); ++i) {
            if (mTransientLaunches.valueAt(i) == task) {
                return true;
                return t == restoreBelow;
            });
            // Add FLAG_ABOVE_TRANSIENT_LAUNCH to the tree of transient-hide tasks,
            // so ChangeInfo#hasChanged() can return true to report the transition info.
            for (int i = mChanges.size() - 1; i >= 0; --i) {
                final WindowContainer<?> wc = mChanges.keyAt(i);
                if (wc.asTaskFragment() == null && wc.asActivityRecord() == null) continue;
                if (isInTransientHide(wc)) {
                    mChanges.valueAt(i).mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
                }
            }
        return false;
        }
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
                + "transient-launch", mSyncId, activity);
    }

    /** @return whether `wc` is a descendent of a transient-hide window. */
    boolean isInTransientHide(@NonNull WindowContainer wc) {
        if (mTransientLaunches == null) return false;
        for (int i = 0; i < mTransientLaunches.size(); ++i) {
            if (wc.isDescendantOf(mTransientLaunches.valueAt(i))) {
        if (mTransientHideTasks == null) return false;
        for (int i = mTransientHideTasks.size() - 1; i >= 0; --i) {
            final Task task = mTransientHideTasks.get(i);
            if (wc == task || wc.isDescendantOf(task)) {
                return true;
            }
        }
@@ -816,6 +839,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
        }
        mController.mFinishingTransition = this;

        if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
            // The transient hide tasks could be occluded now, e.g. returning to home. So trigger
            // the update to make the activities in the tasks invisible-requested, then the next
            // step can continue to commit the visibility.
            mController.mAtm.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
                    0 /* configChanges */, true /* preserveWindows */);
        }

        boolean hasParticipatedDisplay = false;
        boolean hasVisibleTransientLaunch = false;
        // Commit all going-invisible containers
@@ -1175,15 +1206,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {

        // Record windowtokens (activity/wallpaper) that are expected to be visible after the
        // transition animation. This will be used in finishTransition to prevent prematurely
        // committing visibility.
        // committing visibility. Skip transient launches since those are only temporarily visible.
        if (mTransientLaunches == null) {
            for (int i = mParticipants.size() - 1; i >= 0; --i) {
                final WindowContainer wc = mParticipants.valueAt(i);
                if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
            // don't include transient launches, though, since those are only temporarily visible.
            if (mTransientLaunches != null && wc.asActivityRecord() != null
                    && mTransientLaunches.containsKey(wc.asActivityRecord())) continue;
                mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
            }
        }

        // Take task snapshots before the animation so that we can capture IME before it gets
        // transferred. If transition is transient, IME won't be moved during the transition and
+2 −2
Original line number Diff line number Diff line
@@ -366,11 +366,11 @@ class TransitionController {
    }

    boolean isTransientHide(@NonNull Task task) {
        if (mCollectingTransition != null && mCollectingTransition.isTransientHide(task)) {
        if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
            return true;
        }
        for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
            if (mPlayingTransitions.get(i).isTransientHide(task)) return true;
            if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
        }
        return false;
    }
+15 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.isIndependent;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -54,6 +55,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -1400,7 +1402,13 @@ public class TransitionTests extends WindowTestsBase {
        closeTransition.collectExistenceChange(activity1);
        closeTransition.collectExistenceChange(task2);
        closeTransition.collectExistenceChange(activity2);
        closeTransition.setTransientLaunch(activity2, null /* restoreBelow */);
        closeTransition.setTransientLaunch(activity2, task1);
        final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1);
        assertNotNull(task1ChangeInfo);
        assertTrue(task1ChangeInfo.hasChanged());
        final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1);
        assertNotNull(activity1ChangeInfo);
        assertTrue(activity1ChangeInfo.hasChanged());

        activity1.setVisibleRequested(false);
        activity2.setVisibleRequested(true);
@@ -1416,6 +1424,8 @@ public class TransitionTests extends WindowTestsBase {
        verify(snapshotController, times(0)).recordSnapshot(eq(task1), eq(false));

        enteringAnimReports.clear();
        doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
                anyInt(), anyBoolean(), anyBoolean());
        final boolean[] wasInFinishingTransition = { false };
        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
            @Override
@@ -1430,7 +1440,11 @@ public class TransitionTests extends WindowTestsBase {
        assertTrue(wasInFinishingTransition[0]);
        assertNull(controller.mFinishingTransition);

        assertTrue(activity2.isVisible());
        assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState());
        // Because task1 is occluded by task2, finishTransition should make activity1 invisible.
        assertFalse(activity1.isVisibleRequested());
        assertFalse(activity1.isVisible());
        assertFalse(activity1.app.hasActivityInVisibleTask());

        verify(snapshotController, times(1)).recordSnapshot(eq(task1), eq(false));