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

Commit d5b5148b authored by Jiaming Liu's avatar Jiaming Liu
Browse files

Fix TF visibility due to other translucent TF in Task

The original TF visibility logic considers a TF to be visibile whenever
there is another translucent TF above in the same Task. This seems
incorrect when TFs are stacked, e.g. A|B -> B|C (C is translucent and A
is behind B, but A is determined as visible)

Bug: 392991242
Test: atest TaskFragmentOrganizerControllerTest TaskTests
TaskFragmentTest
Flag: EXEMPT bugfix

Change-Id: I7486dd1aaa0d769ce638f71be66e7499c78512cd
parent b088b126
Loading
Loading
Loading
Loading
+20 −7
Original line number Diff line number Diff line
@@ -1407,14 +1407,22 @@ class TaskFragment extends WindowContainer<WindowContainer> {

            final TaskFragment otherTaskFrag = other.asTaskFragment();
            if (otherTaskFrag != null && otherTaskFrag.hasAdjacentTaskFragment()) {
                // For adjacent TaskFragments, we have assumptions that:
                // 1. A set of adjacent TaskFragments always cover the entire Task window, so that
                // if this TaskFragment is behind a set of opaque TaskFragments, then this
                // TaskFragment is invisible.
                // 2. Adjacent TaskFragments do not overlap, so that if this TaskFragment is behind
                // any translucent TaskFragment in the adjacent set, then this TaskFragment is
                // visible behind translucent.
                if (Flags.allowMultipleAdjacentTaskFragments()) {
                    final boolean hasTraversedAdj = otherTaskFrag.forOtherAdjacentTaskFragments(
                            adjacentTaskFragments::contains);
                    if (hasTraversedAdj) {
                        final boolean isTranslucent = otherTaskFrag.isTranslucent(starting)
                                || otherTaskFrag.forOtherAdjacentTaskFragments(adjacentTf -> {
                                    return adjacentTf.isTranslucent(starting);
                                });
                        final boolean isTranslucent =
                                isBehindTransparentTaskFragment(otherTaskFrag, starting)
                                || otherTaskFrag.forOtherAdjacentTaskFragments(
                                        (Predicate<TaskFragment>) tf ->
                                                isBehindTransparentTaskFragment(tf, starting));
                        if (isTranslucent) {
                            // Can be visible behind a translucent adjacent TaskFragments.
                            gotTranslucentFullscreen = true;
@@ -1426,8 +1434,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
                    }
                } else {
                    if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
                        if (otherTaskFrag.isTranslucent(starting)
                                || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
                        if (isBehindTransparentTaskFragment(otherTaskFrag, starting)
                                || isBehindTransparentTaskFragment(
                                        otherTaskFrag.mAdjacentTaskFragment, starting)) {
                            // Can be visible behind a translucent adjacent TaskFragments.
                            gotTranslucentFullscreen = true;
                            gotTranslucentAdjacent = true;
@@ -1439,7 +1448,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
                }
                adjacentTaskFragments.add(otherTaskFrag);
            }

        }

        if (!shouldBeVisible) {
@@ -1452,6 +1460,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
                : TASK_FRAGMENT_VISIBILITY_VISIBLE;
    }

    private boolean isBehindTransparentTaskFragment(
            @NonNull TaskFragment otherTf, @Nullable ActivityRecord starting) {
        return otherTf.isTranslucent(starting) && getBounds().intersect(otherTf.getBounds());
    }

    private static boolean hasRunningActivity(WindowContainer wc) {
        if (wc.asTaskFragment() != null) {
            return wc.asTaskFragment().topRunningActivity() != null;
+71 −0
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;

import static org.junit.Assert.assertEquals;
@@ -254,6 +257,74 @@ public class TaskFragmentTest extends WindowTestsBase {
        assertEquals(true, activityBelow.isVisibleRequested());
    }

    @Test
    public void testVisibilityBehindOpaqueTaskFragment_withTranslucentTaskFragmentInTask() {
        final Task topTask = createTask(mDisplayContent);
        final Rect top = new Rect();
        final Rect bottom = new Rect();
        topTask.getBounds().splitVertically(top, bottom);

        final TaskFragment taskFragmentA = createTaskFragmentWithActivity(topTask);
        final TaskFragment taskFragmentB = createTaskFragmentWithActivity(topTask);
        final TaskFragment taskFragmentC = createTaskFragmentWithActivity(topTask);

        // B and C split the task window. A is behind B. C is translucent.
        taskFragmentA.setBounds(top);
        taskFragmentB.setBounds(top);
        taskFragmentC.setBounds(bottom);
        taskFragmentA.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentB.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentC.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentB.setAdjacentTaskFragments(
                new TaskFragment.AdjacentSet(taskFragmentB, taskFragmentC));
        doReturn(true).when(taskFragmentC).isTranslucent(any());

        // Ensure the activity below is visible
        topTask.ensureActivitiesVisible(null /* starting */);

        // B and C should be visible. A should be invisible.
        assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
                taskFragmentA.getVisibility(null /* starting */));
        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                taskFragmentB.getVisibility(null /* starting */));
        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                taskFragmentC.getVisibility(null /* starting */));
    }

    @Test
    public void testVisibilityBehindTranslucentTaskFragment() {
        final Task topTask = createTask(mDisplayContent);
        final Rect top = new Rect();
        final Rect bottom = new Rect();
        topTask.getBounds().splitVertically(top, bottom);

        final TaskFragment taskFragmentA = createTaskFragmentWithActivity(topTask);
        final TaskFragment taskFragmentB = createTaskFragmentWithActivity(topTask);
        final TaskFragment taskFragmentC = createTaskFragmentWithActivity(topTask);

        // B and C split the task window. A is behind B. B is translucent.
        taskFragmentA.setBounds(top);
        taskFragmentB.setBounds(top);
        taskFragmentC.setBounds(bottom);
        taskFragmentA.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentB.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentC.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        taskFragmentB.setAdjacentTaskFragments(
                new TaskFragment.AdjacentSet(taskFragmentB, taskFragmentC));
        doReturn(true).when(taskFragmentB).isTranslucent(any());

        // Ensure the activity below is visible
        topTask.ensureActivitiesVisible(null /* starting */);

        // A, B and C should be visible.
        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                taskFragmentC.getVisibility(null /* starting */));
        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
                taskFragmentB.getVisibility(null /* starting */));
        assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
                taskFragmentA.getVisibility(null /* starting */));
    }

    @Test
    public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() {
        final ActivityOptions opts = ActivityOptions.makeBasic();