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

Commit 1b50ad68 authored by Jorge Gil's avatar Jorge Gil
Browse files

Adjust TaskFragment#getVisibility calculations

To account for the case of "empty" or "see-through" containers (such as
root freeform tasks with no children or other freeform children), which
were previously returning
TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT and resulted in a
fullscreen sibling behind a root freeform task becoming paused, this
change introduces a couple of WindowContainer concept to help
distinguish these cases:

1) WC#fillsParentBounds: the container fills its parent by bounds or
   policy (i.e. W_M_FULLSCREEN)
2) WC#hasFillingContent: the container has content that fills it, which
   could be translucent or opaque

A freeform root with a non-fullscreen child fillsParentBounds but does
not hasFillingContent, for example, and TF#getVisibility now returns
TASK_FRAGMENT_VISIBILITY_VISIBLE as a result, which allows a sibling
below it to remain resumed.

Flag: com.android.window.flags.enable_see_through_task_fragments
Bug: 407602007
Test: atest ActivityLifecycleFreeformTests
Change-Id: Id3553084a4d8ac8494f61109aba41d896831bc50
parent fbd99f24
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -117,6 +117,8 @@ public enum DesktopExperienceFlags {
            false, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY),
    ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false,
            Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE),
    ENABLE_SEE_THROUGH_TASK_FRAGMENTS(Flags::enableSeeThroughTaskFragments,
            false, Flags.FLAG_ENABLE_SEE_THROUGH_TASK_FRAGMENTS),
    ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, true,
            Flags.FLAG_ENABLE_TASKBAR_CONNECTED_DISPLAYS),
    ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays,
+4 −3
Original line number Diff line number Diff line
@@ -5034,9 +5034,10 @@ class Task extends TaskFragment {

    /** Whether this Task is multi window (exclude PiP) and not filling parent. */
    boolean isNonFullscreenMultiWindow() {
        final int windowingMode = getWindowingMode();
        return windowingMode != WINDOWING_MODE_FULLSCREEN && windowingMode != WINDOWING_MODE_PINNED
                && !fillsParent();
        if (getWindowingMode() == WINDOWING_MODE_PINNED) {
            return false;
        }
        return !fillsParentBounds();
    }

    /**
+13 −3
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.window.DesktopExperienceFlags;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
@@ -420,6 +421,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
            new EnsureActivitiesVisibleHelper(this);

    private final boolean mEnableSeeThroughTaskFragments =
            DesktopExperienceFlags.ENABLE_SEE_THROUGH_TASK_FRAGMENTS.isTrue();

    /** Creates an embedded task fragment. */
    TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
            boolean createdByOrganizer) {
@@ -1332,9 +1336,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
                continue;
            }

            final int otherWindowingMode = other.getWindowingMode();
            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN
                    || (otherWindowingMode != WINDOWING_MODE_PINNED && other.matchParentBounds())) {
            // Must fill the parent to affect visibility.
            boolean affectsSiblingVisibility = other.fillsParentBounds();
            if (mEnableSeeThroughTaskFragments) {
                // It also must have filling content itself, to prevent empty or only partially
                // occluding containers from affecting visibility.
                affectsSiblingVisibility &= other.hasFillingContent();
            }
            if (affectsSiblingVisibility) {
                // This task fragment is fully covered by |other|.
                if (isTranslucent(other, starting)) {
                    // Can be visible behind a translucent TaskFragment.
                    gotTranslucentFullscreen = true;
+49 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.wm;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -1724,6 +1725,54 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        return false;
    }

    /**
     * Returns true if this container fills its parent by policy or bounds.
     *
     * Note: this does not necessarily mean the container "occludes" its siblings or affects their
     * lifecycle. A container may fill its parent but have no content in it, so it would be
     * equivalent to not existing.
     *
     * TODO(b/409417223): Consolidate with {@link #matchParentBounds}.
     */
    boolean fillsParentBounds() {
        final int windowingMode = getWindowingMode();
        return windowingMode == WINDOWING_MODE_FULLSCREEN
                || (windowingMode != WINDOWING_MODE_PINNED && matchParentBounds());
    }

    /**
     * Returns true if this container or its children have content that fills it.
     *
     * Note: a container that fills its parent may not occlude its siblings, such as when it is
     * translucent.
     */
    boolean hasFillingContent() {
        final int childCount = getChildCount();
        if (childCount == 0) {
            return false;
        }
        for (int i = 0; i < childCount; i++) {
            final WindowContainer<?> child = getChildAt(i);
            if (child.fillsParentBounds() && child.hasFillingContent()) {
                // At least one child fills this container and has content filling itself.
                return true;
            }
            if (child.asTaskFragment() != null
                    && child.asTaskFragment().hasAdjacentTaskFragment()) {
                // There's at least one child adjacent task fragment. Consider the parent filling
                // as long as all of the adjacent task fragments have filling content. Whether
                // or not they fill the parent in union is not important.
                final boolean allFillingContent = child.hasFillingContent()
                        && !child.asTaskFragment().forOtherAdjacentTaskFragments(
                            tf -> !tf.hasFillingContent());
                if (allFillingContent) {
                    return true;
                }
            }
        }
        return false;
    }

    /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
    static int computeScreenLayout(int sourceScreenLayout, int screenWidthDp,
            int screenHeightDp) {
+5 −0
Original line number Diff line number Diff line
@@ -5720,6 +5720,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        return mAttrs.type == TYPE_APPLICATION_STARTING;
    }

    @Override
    boolean hasFillingContent() {
        return true;
    }

    @Override
    boolean showWallpaper() {
        if (!isVisibleRequested()
Loading