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

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

Merge "Compatible behavior for non-resizable activity (2/N)"

parents 192fb504 b398da3c
Loading
Loading
Loading
Loading
+104 −0
Original line number Diff line number Diff line
@@ -239,6 +239,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
    ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();

    /**
     * The scale to fit at least one side of the activity to its parent. If the activity uses
     * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
     */
    private float mSizeCompatScale = 1f;
    /**
     * The bounds in global coordinates for activity in size compatibility mode.
     * @see ActivityRecord#inSizeCompatMode
     */
    private Rect mSizeCompatBounds;

    private boolean mDisablePreviewScreenshots;

    private Task mLastParent;
@@ -1563,11 +1574,52 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        return mOrientation;
    }

    /** @return {@code true} if the compatibility bounds is taking effect. */
    boolean inSizeCompatMode() {
        return mSizeCompatBounds != null;
    }

    @Override
    float getSizeCompatScale() {
        return inSizeCompatMode() ? mSizeCompatScale : super.getSizeCompatScale();
    }

    /**
     * @return Non-empty bounds if the activity has override bounds.
     * @see ActivityRecord#resolveOverrideConfiguration(Configuration)
     */
    Rect getResolvedOverrideBounds() {
        // Get bounds from resolved override configuration because it is computed with orientation.
        return getResolvedOverrideConfiguration().windowConfiguration.getBounds();
    }

    @Override
    public void onConfigurationChanged(Configuration newParentConfig) {
        final int prevWinMode = getWindowingMode();
        mTmpPrevBounds.set(getBounds());
        super.onConfigurationChanged(newParentConfig);

        final Task task = getTask();
        final Rect overrideBounds = getResolvedOverrideBounds();
        if (task != null && !overrideBounds.isEmpty()
                // If the changes come from change-listener, the incoming parent configuration is
                // still the old one. Make sure their orientations are the same to reduce computing
                // the compatibility bounds for the intermediate state.
                && getResolvedOverrideConfiguration().orientation == newParentConfig.orientation) {
            final Rect taskBounds = task.getBounds();
            // Since we only center the activity horizontally, if only the fixed height is smaller
            // than its container, the override bounds don't need to take effect.
            if ((overrideBounds.width() != taskBounds.width()
                    || overrideBounds.height() > taskBounds.height())) {
                calculateCompatBoundsTransformation(newParentConfig);
                updateSurfacePosition();
            } else if (mSizeCompatBounds != null) {
                mSizeCompatBounds = null;
                mSizeCompatScale = 1f;
                updateSurfacePosition();
            }
        }

        final int winMode = getWindowingMode();

        if (prevWinMode == winMode) {
@@ -1681,6 +1733,54 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        return mThumbnail;
    }

    /**
     * Calculates the scale and offset to horizontal center the size compatibility bounds into the
     * region which is available to application.
     */
    private void calculateCompatBoundsTransformation(Configuration newParentConfig) {
        final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
        final Rect viewportBounds = parentAppBounds != null
                ? parentAppBounds : newParentConfig.windowConfiguration.getBounds();
        final Rect contentBounds = getResolvedOverrideBounds();
        final float contentW = contentBounds.width();
        final float contentH = contentBounds.height();
        final float viewportW = viewportBounds.width();
        final float viewportH = viewportBounds.height();
        // Only allow to scale down.
        mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
                ? 1 : Math.min(viewportW / contentW, viewportH / contentH);
        final int offsetX = (int) ((viewportW - contentW * mSizeCompatScale + 1) * 0.5f)
                + viewportBounds.left;

        if (mSizeCompatBounds == null) {
            mSizeCompatBounds = new Rect();
        }
        mSizeCompatBounds.set(contentBounds);
        mSizeCompatBounds.offsetTo(0, 0);
        mSizeCompatBounds.scale(mSizeCompatScale);
        mSizeCompatBounds.left += offsetX;
        mSizeCompatBounds.right += offsetX;
    }

    @Override
    public Rect getBounds() {
        if (mSizeCompatBounds != null) {
            return mSizeCompatBounds;
        }
        return super.getBounds();
    }

    @Override
    public boolean matchParentBounds() {
        if (super.matchParentBounds()) {
            return true;
        }
        // An activity in size compatibility mode may have override bounds which equals to its
        // parent bounds, so the exact bounds should also be checked.
        final WindowContainer parent = getParent();
        return parent == null || parent.getBounds().equals(getResolvedOverrideBounds());
    }

    @Override
    void checkAppWindowsReadyToShow() {
        if (allDrawn == mLastAllDrawn) {
@@ -2875,6 +2975,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (mPendingRelaunchCount != 0) {
            pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
        }
        if (mSizeCompatScale != 1f || mSizeCompatBounds != null) {
            pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
                    + mSizeCompatBounds);
        }
        if (mRemovingFromDisplay) {
            pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
        }
+3 −3
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -1839,6 +1838,9 @@ public class WindowManagerService extends IWindowManager.Stub
                return;
            }
            outDisplayFrame.set(win.getDisplayFrameLw());
            if (win.inSizeCompatMode()) {
                outDisplayFrame.scale(win.mInvGlobalScale);
            }
        }
    }

@@ -1963,8 +1965,6 @@ public class WindowManagerService extends IWindowManager.Stub
            if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                    + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
            winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
            win.mEnforceSizeCompat =
                    (win.mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
            if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
                winAnimator.mAlpha = attrs.alpha;
            }
+74 −23
Original line number Diff line number Diff line
@@ -246,7 +246,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    final boolean mIsWallpaper;
    private final boolean mIsFloatingLayer;
    int mSeq;
    boolean mEnforceSizeCompat;
    int mViewVisibility;
    int mSystemUiVisibility;
    /**
@@ -505,7 +504,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
     */
    private PowerManager.WakeLock mDrawLock;

    final private Rect mTmpRect = new Rect();
    private final Rect mTmpRect = new Rect();
    private final Point mTmpPoint = new Point();

    /**
     * Whether the window was resized by us while it was gone for layout.
@@ -655,7 +655,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        mContext = mWmService.mContext;
        DeathRecipient deathRecipient = new DeathRecipient();
        mSeq = seq;
        mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
        mPowerManagerWrapper = powerManagerWrapper;
        mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
        if (localLOGV) Slog.v(
@@ -732,6 +731,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        mSession.windowAddedLocked(mAttrs.packageName);
    }

    /**
     * @return {@code true} if the application runs in size compatibility mode.
     * @see android.content.res.CompatibilityInfo#supportsScreen
     * @see ActivityRecord#inSizeCompatMode
     */
    boolean inSizeCompatMode() {
        return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
                || (mAppToken != null && mAppToken.inSizeCompatMode()
                        // Exclude starting window because it is not displayed by the application.
                        && mAttrs.type != TYPE_APPLICATION_STARTING);
    }

    /**
     * Returns whether this {@link WindowState} has been considered for drawing by its parent.
     */
@@ -995,7 +1006,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);

        mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
        if (mEnforceSizeCompat) {
        if (inSizeCompatMode()) {
            // If there is a size compatibility scale being applied to the
            // window, we need to apply this to its insets so that they are
            // reported to the app in its coordinate space.
@@ -1354,8 +1365,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    }

    void prelayout() {
        if (mEnforceSizeCompat) {
            mGlobalScale = getDisplayContent().mCompatibleScreenScale;
        if (inSizeCompatMode()) {
            mGlobalScale = mToken.getSizeCompatScale();
            mInvGlobalScale = 1 / mGlobalScale;
        } else {
            mGlobalScale = mInvGlobalScale = 1;
@@ -2145,6 +2156,30 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP

    int getSurfaceTouchableRegion(Region region, int flags) {
        final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
        if (mAppToken != null && !mAppToken.getResolvedOverrideBounds().isEmpty()) {
            // There may have touchable letterboxes around the activity, so in order to let the
            // letterboxes are able to receive touch event and slip to activity, the activity with
            // compatibility bounds cannot occupy full screen touchable region.
            if (modal) {
                // A modal window uses the whole compatibility bounds.
                flags |= FLAG_NOT_TOUCH_MODAL;
                mTmpRect.set(mAppToken.getResolvedOverrideBounds());
                // TODO(b/112288258): Remove the forced offset when the override bounds always
                // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}).
                mTmpRect.offsetTo(0, 0);
            } else {
                // Non-modal uses the application based frame.
                mTmpRect.set(mWindowFrames.mCompatFrame);
            }
            // The offset of compatibility bounds is applied to surface of {@link #AppWindowToken}
            // and frame, so it is unnecessary to translate twice in surface based coordinates.
            final int surfaceOffsetX = mAppToken.inSizeCompatMode()
                    ? mAppToken.getBounds().left : 0;
            mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
            region.set(mTmpRect);
            return flags;
        }

        if (modal && mAppToken != null) {
            // Limit the outer touch to the activity stack region.
            flags |= FLAG_NOT_TOUCH_MODAL;
@@ -2943,7 +2978,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
                Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");

            final Rect frame = mWindowFrames.mFrame;
            final Rect frame = mWindowFrames.mCompatFrame;
            final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
            final Rect contentInsets = mWindowFrames.mLastContentInsets;
            final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
@@ -3353,7 +3388,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        pw.println(prefix + "mHasSurface=" + mHasSurface
                + " isReadyForDisplay()=" + isReadyForDisplay()
                + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
        if (mEnforceSizeCompat) {
        if (inSizeCompatMode()) {
            pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
        }
        if (dumpAll) {
@@ -3477,17 +3512,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        float x, y;
        int w,h;

        final boolean inSizeCompatMode = inSizeCompatMode();
        if ((mAttrs.flags & FLAG_SCALED) != 0) {
            if (mAttrs.width < 0) {
                w = pw;
            } else if (mEnforceSizeCompat) {
            } else if (inSizeCompatMode) {
                w = (int)(mAttrs.width * mGlobalScale + .5f);
            } else {
                w = mAttrs.width;
            }
            if (mAttrs.height < 0) {
                h = ph;
            } else if (mEnforceSizeCompat) {
            } else if (inSizeCompatMode) {
                h = (int)(mAttrs.height * mGlobalScale + .5f);
            } else {
                h = mAttrs.height;
@@ -3495,21 +3531,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        } else {
            if (mAttrs.width == MATCH_PARENT) {
                w = pw;
            } else if (mEnforceSizeCompat) {
            } else if (inSizeCompatMode) {
                w = (int)(mRequestedWidth * mGlobalScale + .5f);
            } else {
                w = mRequestedWidth;
            }
            if (mAttrs.height == MATCH_PARENT) {
                h = ph;
            } else if (mEnforceSizeCompat) {
            } else if (inSizeCompatMode) {
                h = (int)(mRequestedHeight * mGlobalScale + .5f);
            } else {
                h = mRequestedHeight;
            }
        }

        if (mEnforceSizeCompat) {
        if (inSizeCompatMode) {
            x = mAttrs.x * mGlobalScale;
            y = mAttrs.y * mGlobalScale;
        } else {
@@ -3537,7 +3573,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        // We need to make sure we update the CompatFrame as it is used for
        // cropping decisions, etc, on systems where we lack a decor layer.
        mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
        if (mEnforceSizeCompat) {
        if (inSizeCompatMode) {
            // See comparable block in computeFrameLw.
            mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
        }
@@ -3650,7 +3686,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP

    float translateToWindowX(float x) {
        float winX = x - mWindowFrames.mFrame.left;
        if (mEnforceSizeCompat) {
        if (inSizeCompatMode()) {
            winX *= mGlobalScale;
        }
        return winX;
@@ -3658,7 +3694,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP

    float translateToWindowY(float y) {
        float winY = y - mWindowFrames.mFrame.top;
        if (mEnforceSizeCompat) {
        if (inSizeCompatMode()) {
            winY *= mGlobalScale;
        }
        return winY;
@@ -4233,12 +4269,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
     */
    void calculatePolicyCrop(Rect policyCrop) {
        final DisplayContent displayContent = getDisplayContent();
        final DisplayInfo displayInfo = displayContent.getDisplayInfo();

        if (!isDefaultDisplay()) {
        if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) {
            // On a different display there is no system decor. Crop the window
            // by the screen boundaries.
            // TODO(multi-display)
            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
            policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
                    mWindowFrames.mCompatFrame.height());
            policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
@@ -4304,7 +4339,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        // scale function because we want to round things to make the crop
        // always round to a larger rect to ensure we don't crop too
        // much and hide part of the window that should be seen.
        if (mEnforceSizeCompat && mInvGlobalScale != 1.0f) {
        if (inSizeCompatMode() && mInvGlobalScale != 1.0f) {
            final float scale = mInvGlobalScale;
            systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
            systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
@@ -4664,8 +4699,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            // Since the parent was outset by its surface insets, we need to undo the outsetting
            // with insetting by the same amount.
            final WindowState parent = getParentWindow();
            outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
                    -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
            transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets);
            outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
                    -parent.mWindowFrames.mFrame.top + mTmpPoint.y);
        } else if (parentWindowContainer != null) {
            final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
            outPoint.offset(-parentBounds.left, -parentBounds.top);
@@ -4683,7 +4719,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        }

        // Expand for surface insets. See WindowState.expandForSurfaceInsets.
        outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
        transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
        outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
    }

    /**
     * The surface insets from layout parameter are in application coordinate. If the window is
     * scaled, the insets also need to be scaled for surface position in global coordinate.
     */
    private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
        if (!inSizeCompatMode()) {
            outPos.x = surfaceInsets.left;
            outPos.y = surfaceInsets.top;
            return;
        }
        outPos.x = (int) (surfaceInsets.left * mGlobalScale + 0.5f);
        outPos.y = (int) (surfaceInsets.top * mGlobalScale + 0.5f);
    }

    boolean needsRelativeLayeringToIme() {
+8 −0
Original line number Diff line number Diff line
@@ -181,6 +181,14 @@ class WindowToken extends WindowContainer<WindowState> {
        }
    }

    /**
     * @return The scale for applications running in compatibility mode. Multiply the size in the
     *         application by this scale will be the size in the screen.
     */
    float getSizeCompatScale() {
        return mDisplayContent.mCompatibleScreenScale;
    }

    /**
     * Returns true if the new window is considered greater than the existing window in terms of
     * z-order.
+43 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;

import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -213,6 +214,48 @@ public class AppWindowTokenTests extends WindowTestsBase {
        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
    }

    @Test
    public void testSizeCompatBounds() {
        // The real surface transaction is unnecessary.
        mToken.setSkipPrepareSurfaces(true);

        final Rect fixedBounds = mToken.getRequestedOverrideConfiguration().windowConfiguration
                .getBounds();
        fixedBounds.set(0, 0, 1200, 1600);
        final Configuration newParentConfig = mTask.getConfiguration();

        // Change the size of the container to two times smaller with insets.
        newParentConfig.windowConfiguration.setAppBounds(200, 0, 800, 800);
        final Rect containerAppBounds = newParentConfig.windowConfiguration.getAppBounds();
        final Rect containerBounds = newParentConfig.windowConfiguration.getBounds();
        containerBounds.set(0, 0, 600, 800);
        mToken.onConfigurationChanged(newParentConfig);

        assertTrue(mToken.inSizeCompatMode());
        assertEquals(containerAppBounds, mToken.getBounds());
        assertEquals((float) containerAppBounds.width() / fixedBounds.width(),
                mToken.getSizeCompatScale(), 0.0001f /* delta */);

        // Change the width of the container to two times bigger.
        containerAppBounds.set(0, 0, 2400, 1600);
        containerBounds.set(containerAppBounds);
        mToken.onConfigurationChanged(newParentConfig);

        assertTrue(mToken.inSizeCompatMode());
        // Don't scale up, so the bounds keep the same as the fixed width.
        assertEquals(fixedBounds.width(), mToken.getBounds().width());
        // Assert the position is horizontal center.
        assertEquals((containerAppBounds.width() - fixedBounds.width()) / 2,
                mToken.getBounds().left);
        assertEquals(1f, mToken.getSizeCompatScale(), 0.0001f  /* delta */);

        // Change the width of the container to fit the fixed bounds.
        containerBounds.set(0, 0, 1200, 2000);
        mToken.onConfigurationChanged(newParentConfig);
        // Assert don't use fixed bounds because the region is enough.
        assertFalse(mToken.inSizeCompatMode());
    }

    @Test
    @Presubmit
    public void testGetOrientation() {
Loading