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

Commit cd5b7a4b authored by Vali Calinescu's avatar Vali Calinescu
Browse files

Pass LetterboxDetails to SysUI

Create LetterboxDetails component to be passed to SysUI so that the status bar scrim can only be shown when needed on large screens when apps are letterboxed.

Fix: 238611867
Test: atest WmTests:SizeCompatTests
Change-Id: I41517a15b3b57452b55b927e18eb2e5acc2fffdd
parent ad2153b3
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -316,12 +316,19 @@ public class DisplayPolicy {
     */
    private final ArrayList<WindowState> mStatusBarBackgroundWindows = new ArrayList<>();

    /**
     * A collection of {@link LetterboxDetails} of all visible activities to be sent to SysUI in
     * order to determine status bar appearance
     */
    private final ArrayList<LetterboxDetails> mLetterboxDetails = new ArrayList<>();

    private String mFocusedApp;
    private int mLastDisableFlags;
    private int mLastAppearance;
    private int mLastBehavior;
    private InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
    private AppearanceRegion[] mLastStatusBarAppearanceRegions;
    private LetterboxDetails[] mLastLetterboxDetails;

    /** The union of checked bounds while building {@link #mStatusBarAppearanceRegionList}. */
    private final Rect mStatusBarColorCheckedBounds = new Rect();
@@ -1629,6 +1636,7 @@ public class DisplayPolicy {
        mNavBarColorWindowCandidate = null;
        mNavBarBackgroundWindow = null;
        mStatusBarAppearanceRegionList.clear();
        mLetterboxDetails.clear();
        mStatusBarBackgroundWindows.clear();
        mStatusBarColorCheckedBounds.setEmpty();
        mStatusBarBackgroundCheckedBounds.setEmpty();
@@ -1708,6 +1716,16 @@ public class DisplayPolicy {
                            win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
                            new Rect(win.getFrame())));
                    mStatusBarColorCheckedBounds.union(sTmpRect);
                    // Check if current activity is letterboxed in order create a LetterboxDetails
                    // component to be passed to SysUI for status bar treatment
                    final ActivityRecord currentActivity = win.getActivityRecord();
                    if (currentActivity != null) {
                        final LetterboxDetails currentLetterboxDetails = currentActivity
                                .mLetterboxUiController.getLetterboxDetails();
                        if (currentLetterboxDetails != null) {
                            mLetterboxDetails.add(currentLetterboxDetails);
                        }
                    }
                }
            }

@@ -2384,12 +2402,15 @@ public class DisplayPolicy {
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        final LetterboxDetails[] letterboxDetails = new LetterboxDetails[mLetterboxDetails.size()];
        mLetterboxDetails.toArray(letterboxDetails);
        if (mLastAppearance == appearance
                && mLastBehavior == behavior
                && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                && Objects.equals(mFocusedApp, focusedApp)
                && mLastFocusIsFullscreen == isFullscreen
                && Arrays.equals(mLastStatusBarAppearanceRegions, statusBarAppearanceRegions)) {
                && Arrays.equals(mLastStatusBarAppearanceRegions, statusBarAppearanceRegions)
                && Arrays.equals(mLastLetterboxDetails, letterboxDetails)) {
            return;
        }
        if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
@@ -2405,9 +2426,10 @@ public class DisplayPolicy {
        mFocusedApp = focusedApp;
        mLastFocusIsFullscreen = isFullscreen;
        mLastStatusBarAppearanceRegions = statusBarAppearanceRegions;
        mLastLetterboxDetails = letterboxDetails;
        callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                appearance, statusBarAppearanceRegions, isNavbarColorManagedByIme, behavior,
                requestedVisibilities, focusedApp, new LetterboxDetails[]{}));
                requestedVisibilities, focusedApp, letterboxDetails));
    }

    private void callStatusBarSafely(Consumer<StatusBarManagerInternal> consumer) {
@@ -2823,6 +2845,12 @@ public class DisplayPolicy {
                pw.print(prefixInner);  pw.println(mLastStatusBarAppearanceRegions[i]);
            }
        }
        if (mLastLetterboxDetails != null) {
            pw.print(prefix); pw.println("mLastLetterboxDetails=");
            for (int i = mLastLetterboxDetails.length - 1; i >= 0; i--) {
                pw.print(prefixInner);  pw.println(mLastLetterboxDetails[i]);
            }
        }
        if (!mStatusBarBackgroundWindows.isEmpty()) {
            pw.print(prefix); pw.println("mStatusBarBackgroundWindows=");
            for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+5 −0
Original line number Diff line number Diff line
@@ -138,6 +138,11 @@ public class Letterbox {
        return mInner;
    }

    /** @return The frame that contains the inner frame and the insets. */
    Rect getOuterFrame() {
        return mOuter;
    }

    /**
     * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can
     * fully cover the window frame.
+32 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.view.WindowManager;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;

import java.io.PrintWriter;
@@ -141,6 +142,15 @@ final class LetterboxUiController {
        }
    }

    /** Gets the outer bounds of letterbox. The bounds will be empty if there is no letterbox. */
    private void getLetterboxOuterBounds(Rect outBounds) {
        if (mLetterbox != null) {
            outBounds.set(mLetterbox.getOuterFrame());
        } else {
            outBounds.setEmpty();
        }
    }

    /**
     * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
     *     when the current activity is displayed.
@@ -683,4 +693,26 @@ final class LetterboxUiController {
        mActivityRecord.mTaskSupervisor.getActivityMetricsLogger()
                .logLetterboxPositionChange(mActivityRecord, letterboxPositionChange);
    }

    @Nullable
    LetterboxDetails getLetterboxDetails() {
        final WindowState w = mActivityRecord.findMainWindow();
        if (mLetterbox == null || w == null || w.isLetterboxedForDisplayCutout()) {
            return null;
        }
        Rect letterboxInnerBounds = new Rect();
        Rect letterboxOuterBounds = new Rect();
        getLetterboxInnerBounds(letterboxInnerBounds);
        getLetterboxOuterBounds(letterboxOuterBounds);

        if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
            return null;
        }

        return new LetterboxDetails(
                letterboxInnerBounds,
                letterboxOuterBounds,
                w.mAttrs.insetsFlags.appearance
        );
    }
}
+104 −0
Original line number Diff line number Diff line
@@ -61,8 +61,10 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
@@ -82,11 +84,14 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.view.InsetsVisibilities;
import android.view.WindowManager;

import androidx.test.filters.MediumTest;

import com.android.internal.policy.SystemBarUtils;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.statusbar.StatusBarManagerInternal;

import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -97,6 +102,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

/**
 * Tests for Size Compatibility mode.
@@ -2186,6 +2192,104 @@ public class SizeCompatTests extends WindowTestsBase {
        assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(organizer.mPrimary.getBounds());
    }

    @Test
    public void testLetterboxDetailsForStatusBar_noLetterbox() {
        setUpDisplaySizeWithApp(2800, 1000);
        addStatusBar(mActivity.mDisplayContent);
        addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
        // appearance inside letterboxDetails

        DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
        StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
        // We should get a null LetterboxDetails object as there is no letterboxed activity, so
        // nothing will get passed to SysUI
        verify(statusBar, never()).onSystemBarAttributesChanged(anyInt(), anyInt(),
                any(), anyBoolean(), anyInt(),
                any(InsetsVisibilities.class), isNull(), isNull());

    }

    @Test
    public void testLetterboxDetailsForStatusBar_letterboxedForMaxAspectRatio() {
        setUpDisplaySizeWithApp(2800, 1000);
        addStatusBar(mActivity.mDisplayContent);
        addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
        // appearance inside letterboxDetails
        // Prepare unresizable activity with max aspect ratio
        prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
        // Refresh the letterbox
        mActivity.mRootWindowContainer.performSurfacePlacement();

        Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
        assertEquals(mBounds, new Rect(850, 0, 1950, 1000));

        DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
        LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
                mBounds,
                mActivity.getDisplayContent().getBounds(),
                mActivity.findMainWindow().mAttrs.insetsFlags.appearance
        )};

        // Check that letterboxDetails actually gets passed to SysUI
        StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
        verify(statusBar).onSystemBarAttributesChanged(anyInt(), anyInt(),
                any(), anyBoolean(), anyInt(),
                any(InsetsVisibilities.class), isNull(), eq(expectedLetterboxDetails));
    }

    @Test
    public void testSplitScreenLetterboxDetailsForStatusBar_twoLetterboxedApps() {
        mAtm.mDevEnableNonResizableMultiWindow = true;
        setUpDisplaySizeWithApp(2800, 1000);
        addStatusBar(mActivity.mDisplayContent);
        // Create another task for the second activity
        final Task newTask = new TaskBuilder(mSupervisor).setDisplay(mActivity.getDisplayContent())
                .setCreateActivity(true).build();
        ActivityRecord newActivity = newTask.getTopNonFinishingActivity();

        final TestSplitOrganizer organizer =
                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());

        // Move first activity to split screen which takes half of the screen.
        organizer.mPrimary.setBounds(0, 0, 1400, 1000);
        organizer.putTaskToPrimary(mTask, true);
        // Move second activity to split screen which takes half of the screen.
        organizer.mSecondary.setBounds(1400, 0, 2800, 1000);
        organizer.putTaskToSecondary(newTask, true);

        addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
        // appearance inside letterboxDetails
        // Prepare unresizable activity with max aspect ratio
        prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
        addWindowToActivity(newActivity);
        prepareUnresizable(newActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);

        // Refresh the letterboxes
        newActivity.mRootWindowContainer.performSurfacePlacement();

        Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
        assertEquals(mBounds, new Rect(150, 0, 1250, 1000));
        final Rect newBounds = new Rect(newActivity.getWindowConfiguration().getBounds());
        assertEquals(newBounds, new Rect(1550, 0, 2650, 1000));

        DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
        LetterboxDetails[] expectedLetterboxDetails = { new LetterboxDetails(
                mBounds,
                organizer.mPrimary.getBounds(),
                mActivity.findMainWindow().mAttrs.insetsFlags.appearance
        ), new LetterboxDetails(
                newBounds,
                organizer.mSecondary.getBounds(),
                newActivity.findMainWindow().mAttrs.insetsFlags.appearance
        )};

        // Check that letterboxDetails actually gets passed to SysUI
        StatusBarManagerInternal statusBar = displayPolicy.getStatusBarManagerInternal();
        verify(statusBar).onSystemBarAttributesChanged(anyInt(), anyInt(),
                any(), anyBoolean(), anyInt(),
                any(InsetsVisibilities.class), isNull(), eq(expectedLetterboxDetails));
    }

    private void recomputeNaturalConfigurationOfUnresizableActivity() {
        // Recompute the natural configuration of the non-resizable activity and the split screen.
        mActivity.clearSizeCompatMode();