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

Commit 8f8baa74 authored by Massimo Carli's avatar Massimo Carli Committed by Android (Google) Code Review
Browse files

Merge "Improve first opaque activity candidate detection" into tm-qpr-dev

parents 00a8c5bc bcf680d0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1612,7 +1612,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                newParent.setResumedActivity(this, "onParentChanged");
                mImeInsetsFrozenUntilStartInput = false;
            }
            mLetterboxUiController.onActivityParentChanged(newParent);
            mLetterboxUiController.updateInheritedLetterbox();
        }

        if (rootTask != null && rootTask.topRunningActivity() == this) {
+48 −33
Original line number Diff line number Diff line
@@ -112,6 +112,8 @@ import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -126,8 +128,7 @@ import java.util.function.Predicate;
final class LetterboxUiController {

    private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing()
                    && activityRecord.nowVisible;
            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing();

    private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;

@@ -185,6 +186,10 @@ final class LetterboxUiController {
    // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS
    private final boolean mIsOverrideEnableCompatFakeFocusEnabled;

    // The list of observers for the destroy event of candidate opaque activities
    // when dealing with translucent activities.
    private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>();

    @Nullable
    private final Boolean mBooleanPropertyAllowOrientationOverride;
    @Nullable
@@ -198,6 +203,10 @@ final class LetterboxUiController {
    @Nullable
    private WindowContainerListener mLetterboxConfigListener;

    @Nullable
    @VisibleForTesting
    ActivityRecord mFirstOpaqueActivityBeneath;

    private boolean mShowWallpaperForLetterboxBackground;

    // In case of transparent activities we might need to access the aspectRatio of the
@@ -361,6 +370,10 @@ final class LetterboxUiController {
            mLetterbox.destroy();
            mLetterbox = null;
        }
        for (int i = mDestroyListeners.size() - 1; i >= 0; i--) {
            mDestroyListeners.get(i).updateInheritedLetterbox();
        }
        mDestroyListeners.clear();
        if (mLetterboxConfigListener != null) {
            mLetterboxConfigListener.onRemoved();
            mLetterboxConfigListener = null;
@@ -1577,7 +1590,11 @@ final class LetterboxUiController {
     * first opaque activity beneath.
     * @param parent The parent container.
     */
    void onActivityParentChanged(WindowContainer<?> parent) {
    void updateInheritedLetterbox() {
        final WindowContainer<?> parent = mActivityRecord.getParent();
        if (parent == null) {
            return;
        }
        if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
            return;
        }
@@ -1587,27 +1604,28 @@ final class LetterboxUiController {
        }
        // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
        // opaque activity constraints because we're expecting the activity is already letterboxed.
        if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
            return;
        }
        final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
        mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
                mActivityRecord /* boundary */, false /* includeBoundary */,
                true /* traverseTopToBottom */);
        if (firstOpaqueActivityBeneath == null || firstOpaqueActivityBeneath.isEmbedded()) {
        if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) {
            // We skip letterboxing if the translucent activity doesn't have any opaque
            // activities beneath or the activity below is embedded which never has letterbox.
            mActivityRecord.recomputeConfiguration();
            return;
        }
        inheritConfiguration(firstOpaqueActivityBeneath);
        if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
            return;
        }
        mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this);
        inheritConfiguration(mFirstOpaqueActivityBeneath);
        mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
                mActivityRecord, firstOpaqueActivityBeneath,
                (opaqueConfig, transparentConfig) -> {
                    final Configuration mutatedConfiguration =
                            fromOriginalTranslucentConfig(transparentConfig);
                mActivityRecord, mFirstOpaqueActivityBeneath,
                (opaqueConfig, transparentOverrideConfig) -> {
                    resetTranslucentOverrideConfig(transparentOverrideConfig);
                    final Rect parentBounds = parent.getWindowConfiguration().getBounds();
                    final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds();
                    final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds();
                    final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
                    // We cannot use letterboxBounds directly here because the position relies on
                    // letterboxing. Using letterboxBounds directly, would produce a double offset.
@@ -1616,9 +1634,9 @@ final class LetterboxUiController {
                            parentBounds.top + letterboxBounds.height());
                    // We need to initialize appBounds to avoid NPE. The actual value will
                    // be set ahead when resolving the Configuration for the activity.
                    mutatedConfiguration.windowConfiguration.setAppBounds(new Rect());
                    inheritConfiguration(firstOpaqueActivityBeneath);
                    return mutatedConfiguration;
                    transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
                    inheritConfiguration(mFirstOpaqueActivityBeneath);
                    return transparentOverrideConfig;
                });
    }

@@ -1691,26 +1709,19 @@ final class LetterboxUiController {
        if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(mActivityRecord.getTask().getActivity(
                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
                mActivityRecord /* boundary */, false /* includeBoundary */,
                true /* traverseTopToBottom */));
        return Optional.ofNullable(mFirstOpaqueActivityBeneath);
    }

    // When overriding translucent activities configuration we need to keep some of the
    // original properties
    private Configuration fromOriginalTranslucentConfig(Configuration translucentConfig) {
        final Configuration configuration = new Configuration(translucentConfig);
    /** Resets the screen size related fields so they can be resolved by requested bounds later. */
    private static void resetTranslucentOverrideConfig(Configuration config) {
        // The values for the following properties will be defined during the configuration
        // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the
        // properties inherited from the first not finishing opaque activity beneath.
        configuration.orientation = ORIENTATION_UNDEFINED;
        configuration.screenWidthDp = configuration.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
        configuration.screenHeightDp =
                configuration.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
        configuration.smallestScreenWidthDp =
                configuration.compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
        return configuration;
        config.orientation = ORIENTATION_UNDEFINED;
        config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
        config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
        config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp =
                SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
    }

    private void inheritConfiguration(ActivityRecord firstOpaque) {
@@ -1729,6 +1740,10 @@ final class LetterboxUiController {
    }

    private void clearInheritedConfig() {
        if (mFirstOpaqueActivityBeneath != null) {
            mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this);
        }
        mFirstOpaqueActivityBeneath = null;
        mLetterboxConfigListener = null;
        mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
        mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+98 −44
Original line number Diff line number Diff line
@@ -174,44 +174,6 @@ public class SizeCompatTests extends WindowTestsBase {
        setUpApp(builder.build());
    }

    @Test
    public void testActivityInHistoryAndNotVisibleIsNotUsedAsOpaqueForTranslucentActivities() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2000, 1000);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = false;
        // Translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        doReturn(false).when(translucentActivity).fillsParent();

        mTask.addChild(translucentActivity);

        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
    }

    @Test
    public void testActivityInHistoryAndVisibleIsUsedAsOpaqueForTranslucentActivities() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2000, 1000);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        // Translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        doReturn(false).when(translucentActivity).fillsParent();

        mTask.addChild(translucentActivity);

        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
    }

    @Test
    public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
@@ -236,7 +198,6 @@ public class SizeCompatTests extends WindowTestsBase {
    public void testHorizontalReachabilityEnabledForTranslucentActivities() {
        setUpDisplaySizeWithApp(2500, 1000);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
        config.setTranslucentLetterboxingOverrideEnabled(true);
        config.setLetterboxHorizontalPositionMultiplier(0.5f);
@@ -312,7 +273,6 @@ public class SizeCompatTests extends WindowTestsBase {
    public void testVerticalReachabilityEnabledForTranslucentActivities() {
        setUpDisplaySizeWithApp(1000, 2500);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
        config.setTranslucentLetterboxingOverrideEnabled(true);
        config.setLetterboxVerticalPositionMultiplier(0.5f);
@@ -384,6 +344,104 @@ public class SizeCompatTests extends WindowTestsBase {
        checkIsCentered.run();
    }

    @Test
    public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2000, 1000);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        // Launch another opaque activity
        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        mTask.addChild(opaqueActivity);
        // Transparent activity strategy not applied
        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());

        // Launch translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        doReturn(false).when(translucentActivity).fillsParent();
        mTask.addChild(translucentActivity);
        // Transparent strategy applied
        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());

        spyOn(translucentActivity.mLetterboxUiController);
        clearInvocations(translucentActivity.mLetterboxUiController);

        // We destroy the first opaque activity
        opaqueActivity.setState(DESTROYED, "testing");
        opaqueActivity.removeImmediately();

        // Check that updateInheritedLetterbox() is invoked again
        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
    }

    @Test
    public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2000, 1000);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);

        // Launch translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        doReturn(false).when(translucentActivity).fillsParent();
        mTask.addChild(translucentActivity);
        // Transparent strategy applied
        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
        assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);

        spyOn(translucentActivity.mLetterboxUiController);
        clearInvocations(translucentActivity.mLetterboxUiController);

        // We destroy the first opaque activity
        mActivity.setState(DESTROYED, "testing");
        mActivity.removeImmediately();

        // Check that updateInheritedLetterbox() is invoked again
        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
        assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
    }

    @Test
    public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2000, 1000);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        // Launch another opaque activity
        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        mTask.addChild(opaqueActivity);
        // Transparent activity strategy not applied
        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());

        // Launch translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
                .build();
        doReturn(false).when(translucentActivity).fillsParent();
        mTask.addChild(translucentActivity);
        // Transparent strategy applied
        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());

        spyOn(translucentActivity.mLetterboxUiController);
        clearInvocations(translucentActivity.mLetterboxUiController);

        // Check that updateInheritedLetterbox() is invoked again
        verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox();
    }

    @Test
    public void testApplyStrategyToTranslucentActivities() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
@@ -391,7 +449,6 @@ public class SizeCompatTests extends WindowTestsBase {
        prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.info.setMinAspectRatio(1.2f);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        // Translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
@@ -448,7 +505,6 @@ public class SizeCompatTests extends WindowTestsBase {
        prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
        mActivity.info.setMinAspectRatio(1.2f);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        // Translucent Activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                .setLaunchedFromUid(mActivity.getUid())
@@ -542,7 +598,6 @@ public class SizeCompatTests extends WindowTestsBase {
                true /* ignoreOrientationRequest */);
        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                1.0f /*letterboxVerticalPositionMultiplier*/);
        mActivity.nowVisible = true;
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
        // We launch a transparent activity
        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
@@ -575,7 +630,6 @@ public class SizeCompatTests extends WindowTestsBase {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2800, 1400);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.nowVisible = true;
        prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
        // Rotate to put activity in size compat mode.
        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);