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

Commit bd8f1ec4 authored by Massimo Carli's avatar Massimo Carli Committed by Android Build Coastguard Worker
Browse files

Improve first opaque activity candidate detection

To catch the right candidate as first opaque activity beneath
a translucent one and handle normal permission dialog
behaviour and smart links.

Fixes: 278661668
Test: Run `atest WmTests:SizeCompatTests`
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bcf680d064ea8921091bfc4620b6958adf79e8a2)
Merged-In: I62e829555c43136080ee4909f7dcf8c388165e9f
Change-Id: I62e829555c43136080ee4909f7dcf8c388165e9f
parent a5cb0053
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);