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

Commit 7c94df49 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Resolve size compatibility bounds by original display attributes" into qt-dev

parents 42a5a8de 7482626a
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -129,8 +129,11 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman

        restartButton = createRestartButton(context);
        restartButton.updateLastTargetActivity(activityToken);
        restartButton.show();
        if (restartButton.show()) {
            mActiveButtons.append(displayId, restartButton);
        } else {
            onDisplayRemoved(displayId);
        }
    }

    @VisibleForTesting
@@ -208,8 +211,16 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman
            mLastActivityToken = activityToken;
        }

        void show() {
        /** @return {@code false} if the target display is invalid. */
        boolean show() {
            try {
                getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
            } catch (WindowManager.InvalidDisplayException e) {
                // The target display may have been removed when the callback has just arrived.
                Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
                return false;
            }
            return true;
        }

        void remove() {
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        doReturn(true).when(mMockButton).show();

        mController = new SizeCompatModeActivityController(mMockAm) {
            @Override
+4 −2
Original line number Diff line number Diff line
@@ -17832,10 +17832,12 @@ public class ActivityManagerService extends IActivityManager.Stub
            synchronized (ActivityManagerService.this) {
                final ProcessRecord proc = getProcessRecordLocked(processName, uid,
                        true /* keepIfLarge */);
                if (proc != null) {
                    mProcessList.removeProcessLocked(proc, false /* callerWillRestart */,
                            true /* allowRestart */, reason);
                }
            }
        }
        @Override
        public boolean hasRunningActivity(int uid, @Nullable String packageName) {
+122 −99
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
@@ -2794,7 +2795,7 @@ final class ActivityRecord extends ConfigurationContainer {
     * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
     *         aspect ratio.
     */
    private boolean shouldUseSizeCompatMode() {
    boolean shouldUseSizeCompatMode() {
        return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
                // The configuration of non-standard type should be enforced by system.
                && isActivityTypeStandard()
@@ -2803,44 +2804,26 @@ final class ActivityRecord extends ConfigurationContainer {

    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
    private void updateOverrideConfiguration() {
        final boolean shouldUseSizeCompatMode = shouldUseSizeCompatMode();
        if (shouldUseSizeCompatMode) {
            if (!matchParentBounds()) {
                // The override configuration is set only once in size compatible mode.
        final Configuration overrideConfig = mTmpConfig;
        if (shouldUseSizeCompatMode()) {
            if (mCompatDisplayInsets != null) {
                // The override configuration is set only once in size compatibility mode.
                return;
            }
            if (!hasProcess() && !isConfigurationCompatible(task.getConfiguration())) {
            final Configuration parentConfig = getParent().getConfiguration();
            if (!hasProcess() && !isConfigurationCompatible(parentConfig)) {
                // Don't compute when launching in fullscreen and the fixed orientation is not the
                // current orientation. It is more accurately to compute the override bounds from
                // the updated configuration after the fixed orientation is applied.
                return;
            }
        }

        computeBounds(mTmpBounds);

        if (shouldUseSizeCompatMode && mTmpBounds.isEmpty()) {
            mTmpBounds.set(task.getWindowConfiguration().getBounds());
        }
        if (mTmpBounds.equals(getRequestedOverrideBounds())) {
            // The bounds is not changed or the activity is resizable (both the 2 bounds are empty).
            return;
        }

        final Configuration overrideConfig = mTmpConfig;
        overrideConfig.unset();
        if (!mTmpBounds.isEmpty()) {
            overrideConfig.windowConfiguration.setBounds(mTmpBounds);
            if (shouldUseSizeCompatMode) {
            // Ensure the screen related fields are set. It is used to prevent activity relaunch
            // when moving between displays. For screenWidthDp and screenWidthDp, because they
            // are relative to bounds and density, they will be calculated in
            // {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
            // relatively fixed.
                final Configuration parentConfig = task.getConfiguration();
                // Don't account decor insets into app bounds.
                mTmpBounds.intersect(parentConfig.windowConfiguration.getAppBounds());
                overrideConfig.windowConfiguration.setAppBounds(mTmpBounds);
            overrideConfig.unset();
            overrideConfig.colorMode = parentConfig.colorMode;
            overrideConfig.densityDpi = parentConfig.densityDpi;
            overrideConfig.screenLayout = parentConfig.screenLayout
@@ -2850,25 +2833,39 @@ final class ActivityRecord extends ConfigurationContainer {
            // and density won't be changed, smallestScreenWidthDp is also fixed.
            overrideConfig.smallestScreenWidthDp = parentConfig.smallestScreenWidthDp;

            // The role of CompatDisplayInsets is like the override bounds.
            final ActivityDisplay display = getDisplay();
            if (display != null && display.mDisplayContent != null) {
                mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent);
            }
        } else {
            // We must base this on the parent configuration, because we set our override
            // configuration's appBounds based on the result of this method. If we used our own
            // configuration, it would be influenced by past invocations.
            computeBounds(mTmpBounds, getParent().getWindowConfiguration().getAppBounds());

            if (mTmpBounds.equals(getRequestedOverrideBounds())) {
                // The bounds is not changed or the activity is resizable (both the 2 bounds are
                // empty).
                return;
            }

            overrideConfig.unset();
            overrideConfig.windowConfiguration.setBounds(mTmpBounds);
        }

        onRequestedOverrideConfigurationChanged(overrideConfig);
    }

    @Override
    void resolveOverrideConfiguration(Configuration newParentConfiguration) {
        // If the activity has override bounds, the relative configuration (e.g. screen size,
        // layout) needs to be resolved according to the bounds.
        final boolean hasOverrideBounds = !matchParentBounds();
        if (hasOverrideBounds && shouldUseSizeCompatMode()) {
        if (mCompatDisplayInsets != null) {
            resolveSizeCompatModeConfiguration(newParentConfiguration);
        } else {
            super.resolveOverrideConfiguration(newParentConfiguration);
            if (hasOverrideBounds) {
            // If the activity has override bounds, the relative configuration (e.g. screen size,
            // layout) needs to be resolved according to the bounds.
            if (!matchParentBounds()) {
                task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
                        newParentConfiguration);
            }
@@ -2881,18 +2878,25 @@ final class ActivityRecord extends ConfigurationContainer {
        getResolvedOverrideConfiguration().seq = mConfigurationSeq;
    }

    /**
     * Resolves consistent screen configuration for orientation and rotation changes without
     * inheriting the parent bounds.
     */
    private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) {
        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
        final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();

        final int parentRotation = newParentConfiguration.windowConfiguration.getRotation();
        final int parentOrientation = newParentConfiguration.orientation;
        int orientation = getConfiguration().orientation;
        if (orientation != newParentConfiguration.orientation
                && isConfigurationCompatible(newParentConfiguration)) {
        if (orientation != parentOrientation && isConfigurationCompatible(newParentConfiguration)) {
            // The activity is compatible to apply the orientation change or it requests different
            // fixed orientation.
            orientation = newParentConfiguration.orientation;
            orientation = parentOrientation;
        } else {
            if (!resolvedBounds.isEmpty()) {
            if (!resolvedBounds.isEmpty()
                    // The decor insets may be different according to the rotation.
                    && getWindowConfiguration().getRotation() == parentRotation) {
                // Keep the computed resolved override configuration.
                return;
            }
@@ -2902,49 +2906,59 @@ final class ActivityRecord extends ConfigurationContainer {
            }
        }

        // The requested override bounds will set to the resolved bounds.
        super.resolveOverrideConfiguration(newParentConfiguration);

        boolean shouldSwapAppBounds = false;
        int width = resolvedBounds.width();
        int height = resolvedBounds.height();
        if ((orientation == ORIENTATION_LANDSCAPE && height > width)
                || (orientation == ORIENTATION_PORTRAIT && width > height)) {
            // Swap width and height because they are opposite to the orientation.
            width = resolvedBounds.height();
            height = resolvedBounds.width();
            // Assume the bounds always starts from zero because the size may be larger than its
            // parent (task ~ display). The actual letterboxing will be done by surface offset.
            resolvedBounds.set(0, 0, width, height);
            shouldSwapAppBounds = true;
        } else if (width == height) {
            // The bounds may contain decor insets, then its app bounds may not be 1:1 and need to
            // be adjusted according to the orientation.
            final int appWidth = resolvedConfig.windowConfiguration.getAppBounds().width();
            final int appHeight = resolvedConfig.windowConfiguration.getAppBounds().height();
            shouldSwapAppBounds = (orientation == ORIENTATION_LANDSCAPE && appHeight > appWidth)
                    || (orientation == ORIENTATION_PORTRAIT && appWidth > appHeight);
        boolean useParentOverrideBounds = false;
        final Rect displayBounds = mTmpBounds;
        final Rect containingAppBounds = new Rect();
        if (task.handlesOrientationChangeFromDescendant()) {
            // Prefer to use the orientation which is determined by this activity to calculate
            // bounds because the parent will follow the requested orientation.
            mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, orientation);
        } else {
            // The parent hierarchy doesn't handle the orientation changes. This is usually because
            // the aspect ratio of display is close to square or the display rotation is fixed.
            // In this case, task will compute override bounds to fit the app with respect to the
            // requested orientation. So here we perform similar calculation to have consistent
            // bounds even the original parent hierarchies were changed.
            final int baseOrientation = task.getParent().getConfiguration().orientation;
            mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, baseOrientation);
            task.computeFullscreenBounds(containingAppBounds, this, displayBounds, baseOrientation);
            useParentOverrideBounds = !containingAppBounds.isEmpty();
        }

        // The offsets will be non-zero if the parent has override bounds.
        final int containingOffsetX = containingAppBounds.left;
        final int containingOffsetY = containingAppBounds.top;
        if (!useParentOverrideBounds) {
            containingAppBounds.set(displayBounds);
        }
        if (parentRotation != ROTATION_UNDEFINED) {
            // Ensure the container bounds won't overlap with the decors.
            TaskRecord.intersectWithInsetsIfFits(containingAppBounds, displayBounds,
                    mCompatDisplayInsets.mNonDecorInsets[parentRotation]);
        }

        computeBounds(resolvedBounds, containingAppBounds);
        if (resolvedBounds.isEmpty()) {
            // Use the entire available bounds because there is no restriction.
            resolvedBounds.set(useParentOverrideBounds ? containingAppBounds : displayBounds);
        } else {
            // The offsets are included in width and height by {@link #computeBounds}, so we have to
            // restore it.
            resolvedBounds.left += containingOffsetX;
            resolvedBounds.top += containingOffsetY;
        }
        task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                mCompatDisplayInsets);

        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
        if (shouldSwapAppBounds) {
            // Preserve the original decor insets (the left and top of the resolved app bounds) if
            // the parent also has the insets at the corresponding side.
            final int left = parentAppBounds.left > 0 ? resolvedAppBounds.top : 0;
            final int top = parentAppBounds.top > 0 ? resolvedAppBounds.left : 0;
            final int appWidth = resolvedAppBounds.height();
            final int appHeight = resolvedAppBounds.width();
            resolvedAppBounds.set(left, top, appWidth + left, appHeight + top);
        }
        // The horizontal inset included in width is not needed if the activity cannot fill the
        // parent, because the offset will be applied by {@link AppWindowToken#mSizeCompatBounds}.
        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
        if (resolvedBounds.width() < parentAppBounds.width()) {
            resolvedBounds.right -= resolvedAppBounds.left;
        }

        task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                mCompatDisplayInsets);
        // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
        // the parent bounds appropriately.
        if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
@@ -3022,7 +3036,7 @@ final class ActivityRecord extends ConfigurationContainer {
     * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
     */
    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
    private void computeBounds(Rect outBounds) {
    private void computeBounds(Rect outBounds, Rect containingAppBounds) {
        outBounds.setEmpty();
        final float maxAspectRatio = info.maxAspectRatio;
        final ActivityStack stack = getActivityStack();
@@ -3038,12 +3052,8 @@ final class ActivityRecord extends ConfigurationContainer {
            return;
        }

        // We must base this on the parent configuration, because we set our override
        // configuration's appBounds based on the result of this method. If we used our own
        // configuration, it would be influenced by past invocations.
        final Rect appBounds = getParent().getWindowConfiguration().getAppBounds();
        final int containingAppWidth = appBounds.width();
        final int containingAppHeight = appBounds.height();
        final int containingAppWidth = containingAppBounds.width();
        final int containingAppHeight = containingAppBounds.height();
        final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
                / (float) Math.min(containingAppWidth, containingAppHeight);

@@ -3109,7 +3119,8 @@ final class ActivityRecord extends ConfigurationContainer {
        // Also account for the left / top insets (e.g. from display cutouts), which will be clipped
        // away later in {@link TaskRecord#computeConfigResourceOverrides()}. Otherwise, the app
        // bounds would end up too small.
        outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top);
        outBounds.set(0, 0, activityWidth + containingAppBounds.left,
                activityHeight + containingAppBounds.top);
    }

    /**
@@ -3468,8 +3479,8 @@ final class ActivityRecord extends ConfigurationContainer {

        // Reset the existing override configuration so it can be updated according to the latest
        // configuration.
        getRequestedOverrideConfiguration().setToDefaults();
        getResolvedOverrideConfiguration().setToDefaults();
        getRequestedOverrideConfiguration().unset();
        getResolvedOverrideConfiguration().unset();
        mCompatDisplayInsets = null;
        if (visible) {
            // Configuration will be ensured when becoming visible, so if it is already visible,
@@ -3826,7 +3837,10 @@ final class ActivityRecord extends ConfigurationContainer {
        final int mDisplayWidth;
        final int mDisplayHeight;

        /** The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. */
        /**
         * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
         * is used to compute the appBounds.
         */
        final Rect[] mNonDecorInsets = new Rect[4];
        /**
         * The stableInsets for each rotation. Includes the status bar inset and the
@@ -3839,24 +3853,33 @@ final class ActivityRecord extends ConfigurationContainer {
            mDisplayWidth = display.mBaseDisplayWidth;
            mDisplayHeight = display.mBaseDisplayHeight;
            final DisplayPolicy policy = display.getDisplayPolicy();
            final DisplayCutout cutout = display.getDisplayInfo().displayCutout;
            for (int rotation = 0; rotation < 4; rotation++) {
                mNonDecorInsets[rotation] = new Rect();
                mStableInsets[rotation] = new Rect();
                final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
                final int dw = rotated ? mDisplayHeight : mDisplayWidth;
                final int dh = rotated ? mDisplayWidth : mDisplayHeight;
                final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
                        .getDisplayCutout();
                policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
                mStableInsets[rotation].set(mNonDecorInsets[rotation]);
                policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
            }
        }

        void getDisplayBounds(Rect outBounds, int rotation) {
        void getDisplayBoundsByRotation(Rect outBounds, int rotation) {
            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
            final int dw = rotated ? mDisplayHeight : mDisplayWidth;
            final int dh = rotated ? mDisplayWidth : mDisplayHeight;
            outBounds.set(0, 0, dw, dh);
        }

        void getDisplayBoundsByOrientation(Rect outBounds, int orientation) {
            final int longSide = Math.max(mDisplayWidth, mDisplayHeight);
            final int shortSide = Math.min(mDisplayWidth, mDisplayHeight);
            final boolean isLandscape = orientation == ORIENTATION_LANDSCAPE;
            outBounds.set(0, 0, isLandscape ? longSide : shortSide,
                    isLandscape ? shortSide : longSide);
        }
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -1758,8 +1758,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
     */
    private void calculateCompatBoundsTransformation(Configuration newParentConfig) {
        final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
        final Rect viewportBounds = parentAppBounds != null
                ? parentAppBounds : newParentConfig.windowConfiguration.getBounds();
        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
        final Rect viewportBounds = parentAppBounds != null ? parentAppBounds : parentBounds;
        final Rect appBounds = getWindowConfiguration().getAppBounds();
        final Rect contentBounds = appBounds != null ? appBounds : getResolvedOverrideBounds();
        final float contentW = contentBounds.width();
@@ -1778,6 +1778,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        mSizeCompatBounds.set(contentBounds);
        mSizeCompatBounds.offsetTo(0, 0);
        mSizeCompatBounds.scale(mSizeCompatScale);
        // Ensure to align the top with the parent.
        mSizeCompatBounds.top = parentBounds.top;
        // The decor inset is included in height.
        mSizeCompatBounds.bottom += viewportBounds.top;
        mSizeCompatBounds.left += offsetX;
Loading