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

Commit 32e60702 authored by Chris Li's avatar Chris Li
Browse files

Check finishing opaque activity for closing TaskFragment

Before, when the TaskFragment and its children Activity is finishing
together, we always consider the TaskFragment as translucent.

Now, when the TaskFragment is closing, we also check if there is any
finishing Activity that is opaque.

Fix: 268433205
Test: atest WmTests:TransitionTests
Change-Id: I6ebeb2713aeb64717e53fb2c507dfb11611e4ec5
parent cce2be38
Loading
Loading
Loading
Loading
+23 −14
Original line number Diff line number Diff line
@@ -904,37 +904,46 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     * starting (about to be visible) activity that is fullscreen (opaque).
     * @param starting The currently starting activity or null if there is none.
     */
    @VisibleForTesting
    boolean isTranslucent(ActivityRecord starting) {
    boolean isTranslucent(@Nullable ActivityRecord starting) {
        if (!isAttached() || isForceHidden() || isForceTranslucent()) {
            return true;
        }
        final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
                PooledLambda.__(ActivityRecord.class), starting);
                PooledLambda.__(ActivityRecord.class), starting, false /* including*/);
        final ActivityRecord opaque = getActivity(p);
        p.recycle();
        return opaque == null;
    }

    private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
        if (r.finishing) {
            // We don't factor in finishing activities when determining translucency since
            // they will be gone soon.
            return false;
    /**
     * Whether the TaskFragment should be treated as translucent for the current transition.
     * This is different from {@link #isTranslucent(ActivityRecord)} as this function also checks
     * finishing activities when the TaskFragment itself is becoming invisible.
     */
    boolean isTranslucentForTransition() {
        if (!isAttached() || isForceHidden() || isForceTranslucent()) {
            return true;
        }
        // Including finishing Activity if the TaskFragment is becoming invisible in the transition.
        final boolean includingFinishing = !isVisibleRequested();
        final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
                PooledLambda.__(ActivityRecord.class), null /* starting */, includingFinishing);
        final ActivityRecord opaque = getActivity(p);
        p.recycle();
        return opaque == null;
    }

    private static boolean isOpaqueActivity(@NonNull ActivityRecord r,
            @Nullable ActivityRecord starting, boolean includingFinishing) {
        if (!r.visibleIgnoringKeyguard && r != starting) {
            // Also ignore invisible activities that are not the currently starting
            // activity (about to be visible).
            return false;
        }

        if (r.occludesParent()) {
            // Root task isn't translucent if it has at least one fullscreen activity
            // that is visible.
            return true;
        }
        return false;
        // TaskFragment isn't translucent if it has at least one fullscreen activity that is
        // visible.
        return r.occludesParent(includingFinishing);
    }

    ActivityRecord getTopNonFinishingActivity() {
+18 −15
Original line number Diff line number Diff line
@@ -1447,23 +1447,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {

    private static boolean isTranslucent(@NonNull WindowContainer wc) {
        final TaskFragment taskFragment = wc.asTaskFragment();
        if (taskFragment != null) {
            if (taskFragment.isTranslucent(null /* starting */)) {
        if (taskFragment == null) {
            return !wc.fillsParent();
        }

        // Check containers differently as they are affected by child visibility.

        if (taskFragment.isTranslucentForTransition()) {
            // TaskFragment doesn't contain occluded ActivityRecord.
            return true;
        }
        final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
        if (adjacentTaskFragment != null) {
                // Treat the TaskFragment as translucent if its adjacent TF is, otherwise everything
                // behind two adjacent TaskFragments are occluded.
                return adjacentTaskFragment.isTranslucent(null /* starting */);
            }
        }
        // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
        //                    it is effected by child visibility but needs to work even
        //                    before visibility is committed. This means refactoring some
        //                    checks to use requested visibility.
            // When the TaskFragment has an adjacent TaskFragment, sibling behind them should be
            // hidden unless any of them are translucent.
            return adjacentTaskFragment.isTranslucentForTransition();
        } else {
            // Non-filling without adjacent is considered as translucent.
            return !wc.fillsParent();
        }
    }

    /**
     * Under some conditions (eg. all visible targets within a parent container are transitioning
+191 −13
Original line number Diff line number Diff line
@@ -700,15 +700,15 @@ public class TransitionTests extends WindowTestsBase {
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task newTask = createTask(mDisplayContent);
        doReturn(false).when(newTask).isTranslucent(any());
        final Task oldTask = createTask(mDisplayContent);
        doReturn(false).when(oldTask).isTranslucent(any());
        final Task newTask = createTask(mDisplayContent);

        final ActivityRecord closing = createActivityRecord(oldTask);
        closing.setOccludesParent(true);
        closing.visibleIgnoringKeyguard = true;
        final ActivityRecord opening = createActivityRecord(newTask);
        opening.setOccludesParent(false);
        opening.setOccludesParent(true);
        opening.visibleIgnoringKeyguard = true;
        // Start states.
        changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
        changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
@@ -716,7 +716,7 @@ public class TransitionTests extends WindowTestsBase {
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, newTask);
        // End states.
        closing.setVisibleRequested(true);
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
@@ -731,8 +731,8 @@ public class TransitionTests extends WindowTestsBase {
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) == 0);
        assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
        assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test
@@ -741,15 +741,15 @@ public class TransitionTests extends WindowTestsBase {
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task newTask = createTask(mDisplayContent);
        doReturn(true).when(newTask).isTranslucent(any());
        final Task oldTask = createTask(mDisplayContent);
        doReturn(false).when(oldTask).isTranslucent(any());
        final Task newTask = createTask(mDisplayContent);

        final ActivityRecord closing = createActivityRecord(oldTask);
        closing.setOccludesParent(true);
        closing.visibleIgnoringKeyguard = true;
        final ActivityRecord opening = createActivityRecord(newTask);
        opening.setOccludesParent(false);
        opening.visibleIgnoringKeyguard = true;
        // Start states.
        changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
        changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
@@ -757,7 +757,7 @@ public class TransitionTests extends WindowTestsBase {
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, newTask);
        // End states.
        closing.setVisibleRequested(true);
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
@@ -772,8 +772,186 @@ public class TransitionTests extends WindowTestsBase {
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) != 0);
        assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
        assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test
    public void testOpenOpaqueTaskFragment() {
        final Transition transition = createTestTransition(TRANSIT_OPEN);
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task task = createTask(mDisplayContent);
        final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
        final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);

        final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
        closing.setOccludesParent(true);
        closing.visibleIgnoringKeyguard = true;
        final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
        opening.setOccludesParent(true);
        opening.visibleIgnoringKeyguard = true;
        // Start states.
        changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
                false /* vis */, true /* exChg */));
        changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
                true /* vis */, false /* exChg */));
        changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, openingTaskFragment);
        // End states.
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
        int flags = 0;

        // Check basic both tasks participating
        participants.add(closingTaskFragment);
        participants.add(openingTaskFragment);
        ArrayList<Transition.ChangeInfo> targets =
                Transition.calculateTargets(participants, changes);
        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test
    public void testOpenTranslucentTaskFragment() {
        final Transition transition = createTestTransition(TRANSIT_OPEN);
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task task = createTask(mDisplayContent);
        final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
        final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);

        final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
        closing.setOccludesParent(true);
        closing.visibleIgnoringKeyguard = true;
        final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
        opening.setOccludesParent(false);
        opening.visibleIgnoringKeyguard = true;
        // Start states.
        changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
                false /* vis */, true /* exChg */));
        changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
                true /* vis */, false /* exChg */));
        changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, openingTaskFragment);
        // End states.
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
        int flags = 0;

        // Check basic both tasks participating
        participants.add(closingTaskFragment);
        participants.add(openingTaskFragment);
        ArrayList<Transition.ChangeInfo> targets =
                Transition.calculateTargets(participants, changes);
        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test
    public void testCloseOpaqueTaskFragment_withFinishingActivity() {
        final Transition transition = createTestTransition(TRANSIT_CLOSE);
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task task = createTask(mDisplayContent);
        final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
        final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);

        final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
        opening.setOccludesParent(true);
        opening.visibleIgnoringKeyguard = true;
        final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
        closing.setOccludesParent(true);
        closing.visibleIgnoringKeyguard = true;
        closing.finishing = true;
        // Start states.
        changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
                false /* vis */, true /* exChg */));
        changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
                true /* vis */, false /* exChg */));
        changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, openingTaskFragment);
        // End states.
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
        int flags = 0;

        // Check basic both tasks participating
        participants.add(closingTaskFragment);
        participants.add(openingTaskFragment);
        ArrayList<Transition.ChangeInfo> targets =
                Transition.calculateTargets(participants, changes);
        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test
    public void testCloseTranslucentTaskFragment_withFinishingActivity() {
        final Transition transition = createTestTransition(TRANSIT_CLOSE);
        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
        ArraySet<WindowContainer> participants = transition.mParticipants;

        final Task task = createTask(mDisplayContent);
        final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
        final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);

        final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
        opening.setOccludesParent(true);
        opening.visibleIgnoringKeyguard = true;
        final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
        closing.setOccludesParent(false);
        closing.visibleIgnoringKeyguard = true;
        closing.finishing = true;
        // Start states.
        changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
                false /* vis */, true /* exChg */));
        changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
                true /* vis */, false /* exChg */));
        changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
        changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
        fillChangeMap(changes, openingTaskFragment);
        // End states.
        closing.setVisibleRequested(false);
        opening.setVisibleRequested(true);

        final int transit = transition.mType;
        int flags = 0;

        // Check basic both tasks participating
        participants.add(closingTaskFragment);
        participants.add(openingTaskFragment);
        ArrayList<Transition.ChangeInfo> targets =
                Transition.calculateTargets(participants, changes);
        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
        assertEquals(2, info.getChanges().size());
        assertEquals(transit, info.getType());

        assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
        assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
    }

    @Test