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

Commit c50509aa authored by Massimo Carli's avatar Massimo Carli Committed by Automerger Merge Worker
Browse files

Merge "Fix scale for translucent activities" into tm-qpr-dev am: 5f267c36...

Merge "Fix scale for translucent activities" into tm-qpr-dev am: 5f267c36 am: 0c1865bb am: 7f19c392

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20980295



Change-Id: Idfb1bc78f46df5b07d85e2763c3d06a903716cd9
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 03f91212 7f19c392
Loading
Loading
Loading
Loading
+56 −38
Original line number Diff line number Diff line
@@ -7986,6 +7986,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return mCompatDisplayInsets;
    }

    /**
     * @return The {@code true} if the current instance has {@link mCompatDisplayInsets} without
     * considering the inheritance implemented in {@link #getCompatDisplayInsets()}
     */
    boolean hasCompatDisplayInsetsWithoutInheritance() {
        return mCompatDisplayInsets != null;
    }

    /**
     * @return {@code true} if this activity is in size compatibility mode that uses the different
     *         density than its parent or its bounds don't fit in parent naturally.
@@ -7994,7 +8002,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (mInSizeCompatModeForBounds) {
            return true;
        }
        if (mCompatDisplayInsets == null || !shouldCreateCompatDisplayInsets()
        if (getCompatDisplayInsets() == null || !shouldCreateCompatDisplayInsets()
                // The orientation is different from parent when transforming.
                || isFixedRotationTransforming()) {
            return false;
@@ -8065,11 +8073,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
    private void updateCompatDisplayInsets() {
        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
            mCompatDisplayInsets =  mLetterboxUiController.getInheritedCompatDisplayInsets();
            return;
        }
        if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
        if (getCompatDisplayInsets() != null || !shouldCreateCompatDisplayInsets()) {
            // The override configuration is set only once in size compatibility mode.
            return;
        }
@@ -8132,9 +8136,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

    @Override
    float getCompatScale() {
        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
            return mLetterboxUiController.getInheritedSizeCompatScale();
        }
        return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
    }

@@ -8181,7 +8182,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            resolveFixedOrientationConfiguration(newParentConfiguration);
        }

        if (mCompatDisplayInsets != null) {
        if (getCompatDisplayInsets() != null) {
            resolveSizeCompatModeConfiguration(newParentConfiguration);
        } else if (inMultiWindowMode() && !isFixedOrientationLetterboxAllowed) {
            // We ignore activities' requested orientation in multi-window modes. They may be
@@ -8199,7 +8200,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            resolveAspectRatioRestriction(newParentConfiguration);
        }

        if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null
        if (isFixedOrientationLetterboxAllowed || getCompatDisplayInsets() != null
                // In fullscreen, can be letterboxed for aspect ratio.
                || !inMultiWindowMode()) {
            updateResolvedBoundsPosition(newParentConfiguration);
@@ -8207,7 +8208,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        boolean isIgnoreOrientationRequest = mDisplayContent != null
                && mDisplayContent.getIgnoreOrientationRequest();
        if (mCompatDisplayInsets == null // for size compat mode set in updateCompatDisplayInsets
        if (getCompatDisplayInsets() == null
                // for size compat mode set in updateCompatDisplayInsets
                // Fixed orientation letterboxing is possible on both large screen devices
                // with ignoreOrientationRequest enabled and on phones in split screen even with
                // ignoreOrientationRequest disabled.
@@ -8253,7 +8255,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                        info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
                        info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
                        !matchParentBounds(),
                        mCompatDisplayInsets != null,
                        getCompatDisplayInsets() != null,
                        shouldCreateCompatDisplayInsets());
            }
            resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
@@ -8560,8 +8562,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                || orientationRespectedWithInsets)) {
            return;
        }
        final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets();

        if (mCompatDisplayInsets != null && !mCompatDisplayInsets.mIsInFixedOrientationLetterbox) {
        if (compatDisplayInsets != null && !compatDisplayInsets.mIsInFixedOrientationLetterbox) {
            // App prefers to keep its original size.
            // If the size compat is from previous fixed orientation letterboxing, we may want to
            // have fixed orientation letterbox again, otherwise it will show the size compat
@@ -8616,9 +8619,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
                containingBounds, desiredAspectRatio);

        if (mCompatDisplayInsets != null) {
            mCompatDisplayInsets.getBoundsByRotation(
                    mTmpBounds, newParentConfig.windowConfiguration.getRotation());
        if (compatDisplayInsets != null) {
            compatDisplayInsets.getBoundsByRotation(mTmpBounds,
                    newParentConfig.windowConfiguration.getRotation());
            if (resolvedBounds.width() != mTmpBounds.width()
                    || resolvedBounds.height() != mTmpBounds.height()) {
                // The app shouldn't be resized, we only do fixed orientation letterboxing if the
@@ -8632,7 +8635,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // Calculate app bounds using fixed orientation bounds because they will be needed later
        // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
        getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
                newParentConfig, mCompatDisplayInsets);
                newParentConfig, compatDisplayInsets);
        mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
    }

@@ -8689,13 +8692,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                ? requestedOrientation
                // We should use the original orientation of the activity when possible to avoid
                // forcing the activity in the opposite orientation.
                : mCompatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
                        ? mCompatDisplayInsets.mOriginalRequestedOrientation
                : getCompatDisplayInsets().mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
                        ? getCompatDisplayInsets().mOriginalRequestedOrientation
                        : newParentConfiguration.orientation;
        int rotation = newParentConfiguration.windowConfiguration.getRotation();
        final boolean isFixedToUserRotation = mDisplayContent == null
                || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
        if (!isFixedToUserRotation && !mCompatDisplayInsets.mIsFloating) {
        if (!isFixedToUserRotation && !getCompatDisplayInsets().mIsFloating) {
            // Use parent rotation because the original display can be rotated.
            resolvedConfig.windowConfiguration.setRotation(rotation);
        } else {
@@ -8711,11 +8714,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // rely on them to contain the original and unchanging width and height of the app.
        final Rect containingAppBounds = new Rect();
        final Rect containingBounds = mTmpBounds;
        mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
        getCompatDisplayInsets().getContainerBounds(containingAppBounds, containingBounds, rotation,
                orientation, orientationRequested, isFixedToUserRotation);
        resolvedBounds.set(containingBounds);
        // The size of floating task is fixed (only swap), so the aspect ratio is already correct.
        if (!mCompatDisplayInsets.mIsFloating) {
        if (!getCompatDisplayInsets().mIsFloating) {
            mIsAspectRatioApplied =
                    applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
        }
@@ -8724,7 +8727,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // are calculated in compat container space. The actual position on screen will be applied
        // later, so the calculation is simpler that doesn't need to involve offset from parent.
        getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                mCompatDisplayInsets);
                getCompatDisplayInsets());
        // Use current screen layout as source because the size of app is independent to parent.
        resolvedConfig.screenLayout = computeScreenLayout(
                getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
@@ -8759,14 +8762,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        // Calculates the scale the size compatibility bounds into the region which is available
        // to application.
        final int contentW = resolvedAppBounds.width();
        final int contentH = resolvedAppBounds.height();
        final int viewportW = containerAppBounds.width();
        final int viewportH = containerAppBounds.height();
        final float lastSizeCompatScale = mSizeCompatScale;
        // Only allow to scale down.
        mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
                ? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
        updateSizeCompatScale(resolvedAppBounds, containerAppBounds);

        final int containerTopInset = containerAppBounds.top - containerBounds.top;
        final boolean topNotAligned =
                containerTopInset != resolvedAppBounds.top - resolvedBounds.top;
@@ -8806,6 +8804,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds);
    }

    void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) {
        // Only allow to scale down.
        mSizeCompatScale = mLetterboxUiController.findOpaqueNotFinishingActivityBelow()
                .map(activityRecord -> activityRecord.mSizeCompatScale)
                .orElseGet(() -> {
                    final int contentW = resolvedAppBounds.width();
                    final int contentH = resolvedAppBounds.height();
                    final int viewportW = containerAppBounds.width();
                    final int viewportH = containerAppBounds.height();
                    return (contentW <= viewportW && contentH <= viewportH) ? 1f : Math.min(
                            (float) viewportW / contentW, (float) viewportH / contentH);
                });
    }

    private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
            // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
@@ -8868,10 +8880,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

    @Override
    public Rect getBounds() {
        // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
        final Rect superBounds = super.getBounds();
        return mLetterboxUiController.findOpaqueNotFinishingActivityBelow()
                .map(ActivityRecord::getBounds)
                .orElseGet(() -> {
                    if (mSizeCompatBounds != null) {
                        return mSizeCompatBounds;
                    }
        return super.getBounds();
                    return superBounds;
                });
    }

    @Override
@@ -8896,7 +8914,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
        // will keep the same bounds and screen configuration when it was first launched regardless
        // how its parent window changes, so that the sandbox API will provide a consistent result.
        if (mCompatDisplayInsets != null || shouldCreateCompatDisplayInsets()) {
        if (getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()) {
            return true;
        }

@@ -8938,7 +8956,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                mTransitionController.collect(this);
            }
        }
        if (mCompatDisplayInsets != null) {
        if (getCompatDisplayInsets() != null) {
            Configuration overrideConfig = getRequestedOverrideConfiguration();
            // Adapt to changes in orientation locking. The app is still non-resizable, but
            // it can change which orientation is fixed. If the fixed orientation changes,
@@ -9014,7 +9032,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (mVisibleRequested) {
            // It may toggle the UI for user to restart the size compatibility mode activity.
            display.handleActivitySizeCompatModeIfNeeded(this);
        } else if (mCompatDisplayInsets != null && !visibleIgnoringKeyguard
        } else if (getCompatDisplayInsets() != null && !visibleIgnoringKeyguard
                && (app == null || !app.hasVisibleActivities())) {
            // visibleIgnoringKeyguard is checked to avoid clearing mCompatDisplayInsets during
            // displays change. Displays are turned off during the change so mVisibleRequested
+20 −20
Original line number Diff line number Diff line
@@ -193,12 +193,6 @@ final class LetterboxUiController {
    // The app compat state for the opaque activity if any
    private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;

    // If true it means that the opaque activity beneath a translucent one is in SizeCompatMode.
    private boolean mIsInheritedInSizeCompatMode;

    // This is the SizeCompatScale of the opaque activity beneath a translucent one
    private float mInheritedSizeCompatScale;

    // The CompatDisplayInsets of the opaque activity beneath the translucent one.
    private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;

@@ -735,8 +729,21 @@ final class LetterboxUiController {
                    : mActivityRecord.inMultiWindowMode()
                            ? mActivityRecord.getTask().getBounds()
                            : mActivityRecord.getRootTask().getParent().getBounds();
            // In case of translucent activities an option is to use the WindowState#getFrame() of
            // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
            // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
            // information and in particular it might provide a value for a smaller area making
            // the letterbox overlap with the translucent activity's frame.
            // If we use WindowState#getFrame() for the translucent activity's letterbox inner
            // frame, the letterbox will then be overlapped with the translucent activity's frame.
            // Because the surface layer of letterbox is lower than an activity window, this
            // won't crop the content, but it may affect other features that rely on values stored
            // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
            // For this reason we use ActivityRecord#getBounds() that the translucent activity
            // inherits from the first opaque activity beneath and also takes care of the scaling
            // in case of activities in size compat mode.
            final Rect innerFrame = hasInheritedLetterboxBehavior()
                    ? mActivityRecord.getWindowConfiguration().getBounds() : w.getFrame();
                    ? mActivityRecord.getBounds() : w.getFrame();
            mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
        } else if (mLetterbox != null) {
            mLetterbox.hide();
@@ -1386,10 +1393,10 @@ final class LetterboxUiController {
            mLetterboxConfigListener.onRemoved();
            clearInheritedConfig();
        }
        // In case mActivityRecord.getCompatDisplayInsets() is not null we don't apply the
        // 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.getCompatDisplayInsets() != null
                || mActivityRecord.fillsParent()) {
        if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
            return;
        }
        final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
@@ -1417,6 +1424,7 @@ final class LetterboxUiController {
                    // 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;
                });
    }
@@ -1457,16 +1465,12 @@ final class LetterboxUiController {
        return mInheritedAppCompatState;
    }

    float getInheritedSizeCompatScale() {
        return mInheritedSizeCompatScale;
    }

    @Configuration.Orientation
    int getInheritedOrientation() {
        return mInheritedOrientation;
    }

    public ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
    ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
        return mInheritedCompatDisplayInsets;
    }

@@ -1486,7 +1490,7 @@ final class LetterboxUiController {
     * @return The first not finishing opaque activity beneath the current translucent activity
     * if it exists and the strategy is enabled.
     */
    private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
    Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
        if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
            return Optional.empty();
        }
@@ -1508,8 +1512,6 @@ final class LetterboxUiController {
        }
        mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation();
        mInheritedAppCompatState = firstOpaque.getAppCompatState();
        mIsInheritedInSizeCompatMode = firstOpaque.inSizeCompatMode();
        mInheritedSizeCompatScale = firstOpaque.getCompatScale();
        mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets();
    }

@@ -1519,8 +1521,6 @@ final class LetterboxUiController {
        mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
        mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
        mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
        mIsInheritedInSizeCompatMode = false;
        mInheritedSizeCompatScale = 1f;
        mInheritedCompatDisplayInsets = null;
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -3389,6 +3389,11 @@ class Task extends TaskFragment {
                && top.getOrganizedTask() == this && top.isState(RESUMED);
        // Whether the direct top activity is in size compat mode on foreground.
        info.topActivityInSizeCompat = isTopActivityResumed && top.inSizeCompatMode();
        if (info.topActivityInSizeCompat
                && mWmService.mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
            // We hide the restart button in case of transparent activities.
            info.topActivityInSizeCompat = top.fillsParent();
        }
        // Whether the direct top activity is eligible for letterbox education.
        info.topActivityEligibleForLetterboxEducation = isTopActivityResumed
                && top.isEligibleForLetterboxEducation();
+9 −3
Original line number Diff line number Diff line
@@ -279,7 +279,8 @@ public class SizeCompatTests extends WindowTestsBase {
    public void testTranslucentActivitiesWhenUnfolding() {
        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
        setUpDisplaySizeWithApp(2800, 1400);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
        mActivity.mDisplayContent.setIgnoreOrientationRequest(
                true /* ignoreOrientationRequest */);
        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                1.0f /*letterboxVerticalPositionMultiplier*/);
        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
@@ -290,18 +291,23 @@ public class SizeCompatTests extends WindowTestsBase {
                .build();
        doReturn(false).when(translucentActivity).fillsParent();
        mTask.addChild(translucentActivity);
        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());

        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        spyOn(mActivity);

        // Halffold
        setFoldablePosture(translucentActivity, true /* isHalfFolded */, false /* isTabletop */);
        setFoldablePosture(translucentActivity, true /* isHalfFolded */,
                false /* isTabletop */);
        verify(mActivity).recomputeConfiguration();
        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
        clearInvocations(mActivity);

        // Unfold
        setFoldablePosture(translucentActivity, false /* isHalfFolded */, false /* isTabletop */);
        setFoldablePosture(translucentActivity, false /* isHalfFolded */,
                false /* isTabletop */);
        verify(mActivity).recomputeConfiguration();
        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
    }

    @Test