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

Commit 23e18a94 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Fix letterbox in fixed rotation transform

- Use rotated bounds as outer frames of letterbox.
- Make sure app bounds related configuration is always
  computed by resolved bounds, so the app bounds won't
  keep a intermediate result while the parent hasn't
  rotated yet.
- Fix freeform size compat activity cannot move outside
  of display left side.

Fix: 151134392
Test: SizeCompatTests#testLaunchWithFixedRotationTransform
      testKeepBoundsWhenChangingFromFreeformToFullscreen

Change-Id: I0615aa4629eefcb1f666482258f6032fe3e89def
parent decfe4b3
Loading
Loading
Loading
Loading
+56 −30
Original line number Diff line number Diff line
@@ -1351,11 +1351,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                mLetterbox.attachInput(w);
            }
            getPosition(mTmpPoint);
            // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
            // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
            // is also applied to the task).
            Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
                    ? task.getDisplayedBounds() : getStack().getDisplayedBounds();
            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
            // priority because the activity is launched in a rotated environment. In multi-window
            // mode, the task-level represents this. In fullscreen-mode, the task container does
            // (since the orientation letterbox is also applied to the task).
            final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
            final Rect spaceToFill = transformedBounds != null
                    ? transformedBounds
                    : inMultiWindowMode()
                            ? task.getDisplayedBounds()
                            : getRootTask().getParent().getDisplayedBounds();
            mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
        } else if (mLetterbox != null) {
            mLetterbox.hide();
@@ -6362,43 +6367,64 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (mCompatDisplayInsets != null) {
            resolveSizeCompatModeConfiguration(newParentConfiguration);
        } else {
            if (inMultiWindowMode()) {
                // We ignore activities' requested orientation in multi-window modes. Task level may
                // take them into consideration when calculating bounds.
            if (inMultiWindowMode()) {
                resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
                // If the activity has requested override bounds, the configuration needs to be
                // computed accordingly.
                if (!matchParentBounds()) {
                    task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
                }
            } else {
                resolveFullscreenConfiguration(newParentConfiguration);
            }
        }

        // Assign configuration sequence number into hierarchy because there is a different way than
        // ensureActivityConfiguration() in this class that uses configuration in WindowState during
        // layout traversals.
        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
        getResolvedOverrideConfiguration().seq = mConfigurationSeq;
    }

    /**
     * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
     * aspect ratio, the position will be centered horizontally in parent's app bounds to balance
     * the visual appearance. The policy of aspect ratio has higher priority than the requested
     * override bounds.
     */
    private void resolveFullscreenConfiguration(Configuration newParentConfiguration) {
        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
            // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
            // use restricted size (resolvedBounds may be the requested override bounds).
        final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
        // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
        // restricted size (resolved bounds may be the requested override bounds).
        mTmpBounds.setEmpty();
        applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
            // If the out bounds is not empty, it means the activity cannot fill parent's app
            // bounds, then the relative configuration (e.g. screen size, layout) needs to be
            // resolved according to the bounds.
            if (!mTmpBounds.isEmpty()) {
                final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
        // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
        // then there is space to be centered.
        final boolean needToBeCentered = !mTmpBounds.isEmpty();
        if (needToBeCentered) {
            resolvedBounds.set(mTmpBounds);
                // Exclude the horizontal decor area because the activity will be centered
                // horizontally in parent's app bounds to balance the visual appearance.
            // Exclude the horizontal decor area.
            resolvedBounds.left = parentAppBounds.left;
        }
        if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
            // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
            // restrict, the bounds should be the requested override bounds.
            task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                    getFixedRotationTransformDisplayInfo());
        }
        if (needToBeCentered) {
            // Offset to center relative to parent's app bounds.
            final int offsetX = getHorizontalCenterOffset(
                    parentAppBounds.width(), resolvedBounds.width());
                if (offsetX > 0) {
                    offsetBounds(resolvedConfig, offsetX - resolvedBounds.left, 0 /* offsetY */);
                }
            offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
        }
    }

        // Assign configuration sequence number into hierarchy because there is a different way than
        // ensureActivityConfiguration() in this class that uses configuration in WindowState during
        // layout traversals.
        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
        getResolvedOverrideConfiguration().seq = mConfigurationSeq;
    }

    /**
     * Resolves consistent screen configuration for orientation and rotation changes without
     * inheriting the parent bounds.
@@ -6507,7 +6533,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // Above coordinates are in "@" space, now place "*" and "#" to screen space.
        final int screenPosX = parentAppBounds.left + offsetX;
        final int screenPosY = parentBounds.top;
        if (screenPosX > 0 || screenPosY > 0) {
        if (screenPosX != 0 || screenPosY != 0) {
            if (mSizeCompatBounds != null) {
                mSizeCompatBounds.offset(screenPosX, screenPosY);
            }
+19 −3
Original line number Diff line number Diff line
@@ -2127,14 +2127,26 @@ class Task extends WindowContainer<WindowContainer> {
        intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
    }

    /**
     * Forces the app bounds related configuration can be computed by
     * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
     * ActivityRecord.CompatDisplayInsets)}.
     */
    private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
        final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
        if (appBounds != null) {
            appBounds.setEmpty();
        }
        inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
        inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
    }

    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
            @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
        if (overrideDisplayInfo != null) {
            // Make sure the screen related configs can be computed by the provided display info.
            inOutConfig.windowConfiguration.setAppBounds(null);
            inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
            invalidateAppBoundsConfig(inOutConfig);
        }
        computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
                null /* compatInsets */);
@@ -2149,6 +2161,10 @@ class Task extends WindowContainer<WindowContainer> {
    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
            @NonNull Configuration parentConfig,
            @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
        if (compatInsets != null) {
            // Make sure the app bounds can be computed by the compat insets.
            invalidateAppBoundsConfig(inOutConfig);
        }
        computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
                compatInsets);
    }
+28 −7
Original line number Diff line number Diff line
@@ -107,10 +107,16 @@ public class SizeCompatTests extends ActivityTestsBase {
        setUpApp(display);

        // Put app window into freeform and then make it a compat app.
        mTask.setBounds(100, 100, 400, 600);
        final Rect bounds = new Rect(100, 100, 400, 600);
        mTask.setBounds(bounds);
        prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
        assertEquals(bounds, mActivity.getBounds());

        // The activity should be able to accept negative x position [-150, 100 - 150, 600].
        final int dx = bounds.left + bounds.width() / 2;
        mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom);
        assertEquals(mTask.getBounds(), mActivity.getBounds());

        final Rect bounds = new Rect(mActivity.getBounds());
        final int density = mActivity.getConfiguration().densityDpi;

        // change display configuration to fullscreen
@@ -231,11 +237,7 @@ public class SizeCompatTests extends ActivityTestsBase {
        // The position should be horizontal centered.
        assertEquals((displayWidth - bounds.width()) / 2, bounds.left);

        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
                mService.mWindowManager, mock(Session.class), new TestIWindow(),
                new WindowManager.LayoutParams(), mActivity);
        mActivity.addWindow(w);
        mActivity.mDisplayContent.mInputMethodTarget = w;
        mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
        // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
        assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
    }
@@ -475,6 +477,25 @@ public class SizeCompatTests extends ActivityTestsBase {
        assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
        // The bounds should be horizontal centered: (2500-1900)/2=350.
        assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);

        // The letterbox needs a main window to layout.
        addWindowToActivity(mActivity);
        // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
        mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */);
        // The letterbox insets should be [350, 0 - 350, 0].
        assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
                mActivity.getLetterboxInsets());
    }

    private WindowState addWindowToActivity(ActivityRecord activity) {
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
                mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
        WindowTestsBase.makeWindowVisible(w);
        w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
        mActivity.addWindow(w);
        return w;
    }

    /**