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

Commit 80f04dde authored by Simon (Qiong) Sun's avatar Simon (Qiong) Sun
Browse files

Correct thumbnail clipping in landscape 10% split

Addresses an issue where thumbnails were incorrectly clipped in landscape split-screen, particularly with 10% ratios. This occurred due to miscalculated post-rotation translation, leading to content being drawn out of bounds.

Key changes:
- Consistent Rotation: Thumbnails are now always rotated when in split-screen configuration.
- Dynamic Translation Adjustment: Introduces a specific translation adjustment for `postTranslate.dy` that accounts for clipping in horizontal split-screen scenarios. This adjustment utilizes the `splitThumbnailClipHint` to precisely position the thumbnail.

The adjustment applies under the following conditions to mitigate regression risk:
- Split screen mode is active.
- Applications are horizontally stacked (`!appsStackedVertically`).
- Device is rotated 90 degrees and split is 10% on the left (`SNAP_TO_2_10_90`, `SPLIT_POSITION_TOP_OR_LEFT`).
- OR device is rotated 270 degrees and split is 10% on the right (`SNAP_TO_2_90_10`, `SPLIT_POSITION_BOTTOM_OR_RIGHT`).

Bug: 426438170
Flag: com.android.wm.shell.enable_flexible_two_app_split
Video: After: http://recall/-/gvQq7Z7JECwxnxKrBmr3wt/OuOFKIm7Rr54b7cCj8kXU
Test: Manual
Change-Id: I80ab2df3cde5a6a57cff3824d8fa9bb7546dec2e
parent 00b768f8
Loading
Loading
Loading
Loading
+71 −8
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;

@@ -52,6 +54,7 @@ public class PreviewPositionHelper {
        return mIsOrientationChanged;
    }

    // TODO: b/428707131 - Pass-in visibleRect of thumbnail, removing setSplitBounds method.
    /**
     * Updates the matrix based on the provided parameters
     */
@@ -66,6 +69,7 @@ public class PreviewPositionHelper {
        RectF thumbnailClipHint = new RectF();
        float scale = thumbnailData.scale;
        final float thumbnailScale;
        float rotationYClipHint = 0;

        // Landscape vs portrait change.
        // Note: Disable rotation in grid layout.
@@ -93,7 +97,7 @@ public class PreviewPositionHelper {
            boolean isAspectLargelyDifferent =
                    Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
                            availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
            if (isRotated && isAspectLargelyDifferent) {
            if (isRotated && !shouldRotateThumbnail(isAspectLargelyDifferent)) {
                // Do not rotate thumbnail if it would not improve fit
                isRotated = false;
                isOrientationDifferent = false;
@@ -165,7 +169,6 @@ public class PreviewPositionHelper {
            }

            thumbnailScale = targetW / (croppedWidth * scale);

            if (mSplitBounds != null
                    && mSplitBounds.snapPosition == SNAP_TO_2_10_90
                    && mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
@@ -175,14 +178,19 @@ public class PreviewPositionHelper {
                    thumbnailClipHint.left += availableWidth - croppedWidth;
                }
            }
            // Adjust translation for specific horizontal split-screen scenarios
            // where clipping occurs.
            if (shouldApplySplitScreenClippingAdjustment(deltaRotate)) {
                rotationYClipHint = availableWidth - croppedWidth;
            }
        }

        if (!isRotated) {
        if (isRotated) {
            setThumbnailRotation(deltaRotate, thumbnailBounds, rotationYClipHint);
        } else {
            mMatrix.setTranslate(
                    -thumbnailClipHint.left * scale,
                    -thumbnailClipHint.top * scale);
        } else {
            setThumbnailRotation(deltaRotate, thumbnailBounds);
        }

        mMatrix.postScale(thumbnailScale, thumbnailScale);
@@ -204,11 +212,12 @@ public class PreviewPositionHelper {
        return deltaRotation == ROTATION_90 || deltaRotation == ROTATION_270;
    }

    private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
    private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition,
            float rotationYClipHint) {
        mMatrix.setRotate(90 * deltaRotate);

        float translateX = 0;
        float translateY = 0;

        mMatrix.setRotate(90 * deltaRotate);
        switch (deltaRotate) { /* Counter-clockwise */
            case ROTATION_90:
                translateX = thumbnailPosition.height();
@@ -221,9 +230,63 @@ public class PreviewPositionHelper {
                translateY = thumbnailPosition.height();
                break;
        }
        // Adjust translationY with clip hint.
        translateY -= rotationYClipHint;
        mMatrix.postTranslate(translateX, translateY);
    }

    /**
     * Determines whether the thumbnail should be rotated based on aspect ratio differences
     * and split-screen conditions.
     *
     * @param isAspectLargelyDifferent True if the aspect ratio of the task is significantly
     *                                 different from the current display, false otherwise.
     * @return True if the thumbnail should be rotated, false otherwise.
     */
    private boolean shouldRotateThumbnail(boolean isAspectLargelyDifferent) {
        // If aspect ratio is not largely different, always rotate.
        if (!isAspectLargelyDifferent) {
            return true;
        }

        // If no split screen, always not rotate.
        if (mSplitBounds == null) {
            return false;
        }

        // Aspect is different AND split screen is active, check specific split conditions
        boolean isTopLeftSnap = (mSplitBounds.snapPosition == SNAP_TO_2_10_90
                && mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT);
        boolean isBottomRightSnap = (mSplitBounds.snapPosition == SNAP_TO_2_90_10
                && mSplitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT);

        return isTopLeftSnap || isBottomRightSnap;
    }

    /**
     * Checks if a special translation adjustment is needed for split-screen clipping.
     * This applies when apps are stacked horizontally and specific rotation/snap
     * positions occur, causing part of the thumbnail to be clipped.
     *
     * @param deltaRotate The current rotation delta.
     * @return {@code true} if the adjustment should be applied, {@code false} otherwise.
     */
    private boolean shouldApplySplitScreenClippingAdjustment(int deltaRotate) {
        if (mSplitBounds == null || mSplitBounds.appsStackedVertically) {
            return false;
        }

        boolean isRotated90AndLeftSnapped = (deltaRotate == ROTATION_90
                && mSplitBounds.snapPosition == SNAP_TO_2_10_90
                && mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT);

        boolean isRotated270AndRightSnapped = (deltaRotate == ROTATION_270
                && mSplitBounds.snapPosition == SNAP_TO_2_90_10
                && mSplitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT);

        return isRotated90AndLeftSnapped || isRotated270AndRightSnapped;
    }

    public void setSplitBounds(SplitBounds splitBounds, int stagePosition) {
        mSplitBounds = splitBounds;
        mSplitPosition = stagePosition;