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

Commit 48ed9e63 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[3/N] Desks: Adjust root task opacity calculation" into main

parents 12f4df91 4787b0a7
Loading
Loading
Loading
Loading
+67 −17
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.SaferIntentUtils;
import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.window.flags.Flags;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -278,7 +279,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
    /** Helper for {@link Task#fillTaskInfo}. */
    final TaskInfoHelper mTaskInfoHelper = new TaskInfoHelper();

    final OpaqueActivityHelper mOpaqueActivityHelper = new OpaqueActivityHelper();
    final OpaqueContainerHelper mOpaqueContainerHelper = new OpaqueContainerHelper();

    private final ActivityTaskSupervisorHandler mHandler;
    final Looper mLooper;
@@ -2913,41 +2914,90 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
        }
    }

    /** The helper to get the top opaque activity of a container. */
    static class OpaqueActivityHelper implements Predicate<ActivityRecord> {
    /** The helper to calculate whether a container is opaque. */
    static class OpaqueContainerHelper implements Predicate<ActivityRecord> {
        private ActivityRecord mStarting;
        private boolean mIncludeInvisibleAndFinishing;
        private boolean mIgnoringInvisibleActivity;
        private boolean mIgnoringKeyguard;

        ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) {
            mIncludeInvisibleAndFinishing = true;
            mIgnoringKeyguard = true;
            return container.getActivity(this,
                    true /* traverseTopToBottom */, null /* boundary */);
        /** Whether the container is opaque. */
        boolean isOpaque(@NonNull WindowContainer<?> container) {
            return isOpaque(container, null /* starting */, true /* ignoringKeyguard */,
                    false /* ignoringInvisibleActivity */);
        }

        ActivityRecord getVisibleOpaqueActivity(
        /**
         * Whether the container is opaque, but only including visible activities in its
         * calculation.
         */
        boolean isOpaque(
                @NonNull WindowContainer<?> container, @Nullable ActivityRecord starting,
                boolean ignoringKeyguard) {
                boolean ignoringKeyguard,  boolean ignoringInvisibleActivity) {
            mStarting = starting;
            mIncludeInvisibleAndFinishing = false;
            mIgnoringInvisibleActivity = ignoringInvisibleActivity;
            mIgnoringKeyguard = ignoringKeyguard;
            final ActivityRecord opaque = container.getActivity(this,
                    true /* traverseTopToBottom */, null /* boundary */);

            final boolean isOpaque;
            if (!Flags.enableMultipleDesktopsBackend()) {
                isOpaque = container.getActivity(this,
                        true /* traverseTopToBottom */, null /* boundary */) != null;
            } else {
                isOpaque = isOpaqueInner(container);
            }
            mStarting = null;
            return opaque;
            return isOpaque;
        }

        private boolean isOpaqueInner(@NonNull WindowContainer<?> container) {
            // If it's a leaf task fragment, then opacity is calculated based on its activities.
            if (container.asTaskFragment() != null
                    && ((TaskFragment) container).isLeafTaskFragment()) {
                return container.getActivity(this,
                        true /* traverseTopToBottom */, null /* boundary */) != null;
            }
            // When not a leaf, it's considered opaque if any of its opaque children fill this
            // container, unless the children are adjacent fragments, in which case as long as they
            // are all opaque then |container| is also considered opaque, even if the adjacent
            // task fragment aren't filling.
            for (int i = 0; i < container.getChildCount(); i++) {
                final WindowContainer<?> child = container.getChildAt(i);
                if (child.fillsParent() && isOpaque(child)) {
                    return true;
                }

                if (child.asTaskFragment() != null
                        && child.asTaskFragment().hasAdjacentTaskFragment()) {
                    final boolean isAnyTranslucent;
                    if (Flags.allowMultipleAdjacentTaskFragments()) {
                        final TaskFragment.AdjacentSet set =
                                child.asTaskFragment().getAdjacentTaskFragments();
                        isAnyTranslucent = set.forAllTaskFragments(
                                tf -> !isOpaque(tf), null);
                    } else {
                        final TaskFragment adjacent = child.asTaskFragment()
                                .getAdjacentTaskFragment();
                        isAnyTranslucent = !isOpaque(child) || !isOpaque(adjacent);
                    }
                    if (!isAnyTranslucent) {
                        // This task fragment and all its adjacent task fragments are opaque,
                        // consider it opaque even if it doesn't fill its parent.
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean test(ActivityRecord r) {
            if (!mIncludeInvisibleAndFinishing && r != mStarting
            if (mIgnoringInvisibleActivity && r != mStarting
                    && ((mIgnoringKeyguard && !r.visibleIgnoringKeyguard)
                    || (!mIgnoringKeyguard && !r.isVisible()))) {
                // Ignore invisible activities that are not the currently starting activity
                // (about to be visible).
                return false;
            }
            return r.occludesParent(mIncludeInvisibleAndFinishing /* includingFinishing */);
            return r.occludesParent(!mIgnoringInvisibleActivity /* includingFinishing */);
        }
    }

+5 −7
Original line number Diff line number Diff line
@@ -1195,10 +1195,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        if (!isAttached() || isForceHidden() || isForceTranslucent()) {
            return true;
        }
        // A TaskFragment isn't translucent if it has at least one visible activity that occludes
        // this TaskFragment.
        return mTaskSupervisor.mOpaqueActivityHelper.getVisibleOpaqueActivity(this,
                starting, true /* ignoringKeyguard */) == null;
        return !mTaskSupervisor.mOpaqueContainerHelper.isOpaque(
                this, starting, true /* ignoringKeyguard */, true /* ignoringInvisibleActivity */);
    }

    /**
@@ -1211,7 +1209,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) == null;
        return !mTaskSupervisor.mOpaqueContainerHelper.isOpaque(this);
    }

    /**
@@ -1222,8 +1220,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        if (!isAttached() || isForceHidden() || isForceTranslucent()) {
            return true;
        }
        return mTaskSupervisor.mOpaqueActivityHelper.getVisibleOpaqueActivity(this, null,
                false /* ignoringKeyguard */) == null;
        return !mTaskSupervisor.mOpaqueContainerHelper.isOpaque(this, /* starting */ null,
                false /* ignoringKeyguard */, true /* ignoringInvisibleActivity */);
    }

    ActivityRecord getTopNonFinishingActivity() {
+1 −1
Original line number Diff line number Diff line
@@ -505,7 +505,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
                final WindowContainer<?> sibling = rootParent.getChildAt(j);
                if (sibling == transientRoot) break;
                if (!sibling.getWindowConfiguration().isAlwaysOnTop() && mController.mAtm
                        .mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(sibling) != null) {
                        .mTaskSupervisor.mOpaqueContainerHelper.isOpaque(sibling)) {
                    occludedCount++;
                    break;
                }
+98 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -44,20 +45,26 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;

import android.annotation.NonNull;
import android.app.ActivityOptions;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Binder;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.Display;

import androidx.test.filters.MediumTest;

import com.android.window.flags.Flags;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
@@ -424,4 +431,95 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
        assertThat(activity.mLaunchCookie).isNull();
        verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
    }

    @Test
    public void testOpaque_leafTask_occludingActivity_isOpaque() {
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        activity.setOccludesParent(true);
        final TaskFragment tf = activity.getTaskFragment();

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(tf)).isTrue();
    }

    @Test
    public void testOpaque_leafTask_nonOccludingActivity_isTranslucent() {
        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
        activity.setOccludesParent(false);
        final TaskFragment tf = activity.getTaskFragment();

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(tf)).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    public void testOpaque_rootTask_translucentFillingChild_isTranslucent() {
        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
        createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_FREEFORM, /* opaque */ false, /* filling */ true);

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    public void testOpaque_rootTask_opaqueAndNotFillingChild_isTranslucent() {
        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
        createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_FREEFORM, /* opaque */ true, /* filling */ false);

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    public void testOpaque_rootTask_opaqueAndFillingChild_isOpaque() {
        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
        createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_FREEFORM, /* opaque */ true, /* filling */ true);

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    public void testOpaque_rootTask_nonFillingOpaqueAdjacentChildren_isOpaque() {
        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
        final TaskFragment tf1 = createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
        final TaskFragment tf2 = createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
        tf1.setAdjacentTaskFragment(tf2);

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isTrue();
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
            Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS})
    public void testOpaque_rootTask_nonFillingOpaqueAdjacentChildren_multipleAdjacent_isOpaque() {
        final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
        final TaskFragment tf1 = createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
        final TaskFragment tf2 = createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
        final TaskFragment tf3 = createChildTaskFragment(/* parent */ rootTask,
                WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
        tf1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf1, tf2, tf3));

        assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isTrue();
    }

    @NonNull
    private TaskFragment createChildTaskFragment(@NonNull Task parent,
            @WindowConfiguration.WindowingMode int windowingMode,
            boolean opaque,
            boolean filling) {
        final ActivityRecord activity = new ActivityBuilder(mAtm)
                .setCreateTask(true).setParentTask(parent).build();
        activity.setOccludesParent(opaque);
        final TaskFragment tf = activity.getTaskFragment();
        tf.setWindowingMode(windowingMode);
        tf.setBounds(filling ? new Rect() : new Rect(100, 100, 200, 200));
        return tf;
    }
}