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

Commit a894adaa authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Fix bug with 90:10 snapping to 70:30 on rotation

This happens on certain devices when insets are large. Previously, when rotating, the device, we relied on the int position of the divider to help us find the new snap position. However, this could sometimes result in snapping to the wrong snap point -- e.g. a 90% snap point snapping to a 70% snap point when insets were larger in the new orientation.

Fixed by changing several things:
- In flexible split mode, when offscreen ratios are permitted, there will now always be a 10%, 33%, 50%, 66%, and 90% snap point. We will size up the 33% and 66% app a little if the app would otherwise be too small. This was discussed in UX -- in flex split we no longer want to delete the 33% snap point when the screen is too small. (Behavior will remain the same for legacy non-flex split).
- On rotation, initDividerPosition() now tries to match the old SnapPosition if it can, rather than relying on the position only. It will still fall back to the old behavior if this fails.
- SplitState is now updated when initDividerPosition() is called (previously had a subtle bug where the SnapPosition could change on rotation but SplitState was not updated).

Fixes: 420545909
Flag: com.android.wm.shell.enable_flexible_two_app_split
Test: Manual test with affected devices
Change-Id: I80ddad20f859073fab29b48ec4926c0b7a279a37
parent 84512d9b
Loading
Loading
Loading
Loading
+29 −26
Original line number Diff line number Diff line
@@ -418,7 +418,7 @@ public class DividerSnapAlgorithm {
        int pinnedTaskbarShiftEnd = mIsLeftRightSplit
                ? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;

        // If offscreen apps are supported, we are looking to add 3-5 targets.
        // If offscreen apps are supported, we are looking to add 5 targets.
        if (areOffscreenRatiosSupported()) {
            // Find the desired sizes for a 10% app and a 33% app.
            float ratio10 = SplitSpec.OFFSCREEN_ASYMMETRIC_RATIO;
@@ -434,6 +434,8 @@ public class DividerSnapAlgorithm {
            int size10 = (int) (ratio10 * dividerMax) + extraSpaceFor10 - mDividerSize / 2;
            // For the 33% target, we bake the insets into the position calculation below.
            int size33 = (int) (ratio33 * (end - start)) - mDividerSize / 2;
            // If the resulting size is too small, bump it up to the minimum required size.
            size33 = Math.max(size33, mMinimalSizeResizableTask);

            int leftTop10Position = size10;
            int rightBottom10Position = dividerMax - size10 - mDividerSize;
@@ -441,44 +443,32 @@ public class DividerSnapAlgorithm {
            int rightBottom33Position = end - size33 - mDividerSize;

            // Get the desired layout for our current device/display/rotation.
            boolean bigEnoughFor33 = size33 >= mMinimalSizeResizableTask;
            List<Integer> targetSpec = SplitSpec.getSnapTargetLayout(SNAP_FLEXIBLE_HYBRID,
                    areOffscreenRatiosSupported(), bigEnoughFor33);
                    areOffscreenRatiosSupported(), true /* bigEnoughFor33 */);

            if (bigEnoughFor33) {
            // Add 5 targets
            addNonDismissingTargets(List.of(leftTop10Position, leftTop33Position,
                            getMiddleTargetPos(), rightBottom33Position, rightBottom10Position),
                    targetSpec);
            } else {
                // Add 3 targets
                addNonDismissingTargets(List.of(leftTop10Position, getMiddleTargetPos(),
                                rightBottom10Position),
                        targetSpec);
            }
        } else {
            // If offscreen apps are not supported, just add the regular 1-3 targets.
            float ratio = SplitSpec.ONSCREEN_ONLY_ASYMMETRIC_RATIO;

            // The intended size of the smaller app, in pixels
            int size = (int) (ratio * (end - start)) - mDividerSize / 2;
            // If the resulting size is too small, bump it up to the minimum required size.
            size = Math.max(size, mMinimalSizeResizableTask);

            int leftTopPosition = start + size;
            int rightBottomPosition = end - size - mDividerSize;

            // Get the desired layout for our current device/display/rotation.
            boolean bigEnoughFor33 = size >= mMinimalSizeResizableTask;
            List<Integer> targetSpec = SplitSpec.getSnapTargetLayout(SNAP_FLEXIBLE_HYBRID,
                    areOffscreenRatiosSupported(), bigEnoughFor33);
                    areOffscreenRatiosSupported(), true /* bigEnoughFor33 */);

            if (bigEnoughFor33) {
            // Add 3 targets
            addNonDismissingTargets(List.of(leftTopPosition, getMiddleTargetPos(),
                    rightBottomPosition), targetSpec);
            } else {
                // Add 1 target
                addNonDismissingTargets(List.of(getMiddleTargetPos()), targetSpec);
            }
        }
    }

@@ -566,6 +556,19 @@ public class DividerSnapAlgorithm {
        return snap(currentPosition, /* hardDismiss */ true).snapPosition;
    }

    /**
     * Gets the on-screen position of a SnapTarget matching the provided @SnapPosition, if one
     * exists. If not, return null.
     */
    public Integer getPositionBySnapPosition(@SnapPosition int snapPosition) {
        for (SnapTarget t : mTargets) {
            if (t.snapPosition == snapPosition) {
                return t.getPosition();
            }
        }
        return null;
    }

    @Nullable
    public MotionSpec getMotionSpec(Resources resources) {
        if (Flags.enableFlexibleTwoAppSplit() && mMotionSpec == null) {
+16 −3
Original line number Diff line number Diff line
@@ -575,11 +575,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
        // Estimate position by previous ratio.
        final float length =
                (float) (mIsLeftRightSplit ? mRootBounds.width() : mRootBounds.height());
        final int estimatePosition = (int) (length * snapRatio);
        int estimatedPosition = (int) (length * snapRatio);

        if (Flags.enableFlexibleTwoAppSplit()) {
            // If we are able to find an exact match for the previous snapPosition (before
            // rotation), use it. If not, just rely on the position estimate.
            int previousState = mSplitState.get();
            Integer exactPosition = mDividerSnapAlgorithm.getPositionBySnapPosition(previousState);
            if (exactPosition != null) {
                estimatedPosition = exactPosition;
            }
        }

        // Init divider position by estimated position using current bounds snap algorithm.
        mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
                estimatePosition).position;
        SnapTarget newSnapTarget = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
                estimatedPosition);
        mDividerPosition = newSnapTarget.position;
        updateBounds(mDividerPosition);
        mSplitState.set(newSnapTarget.snapPosition);
    }

    private void updateBounds(int position) {
+1 −5
Original line number Diff line number Diff line
@@ -231,11 +231,7 @@ public class SplitSpec {
            case SNAP_FIXED_RATIO:
                return bigEnoughFor33 ? THREE_TARGETS_ONSCREEN : ONE_TARGET;
            case SNAP_FLEXIBLE_HYBRID:
                if (areOffscreenRatiosSupported) {
                    return bigEnoughFor33 ? FIVE_TARGETS : THREE_TARGETS_OFFSCREEN;
                } else {
                    return bigEnoughFor33 ? THREE_TARGETS_ONSCREEN : ONE_TARGET;
                }
                return areOffscreenRatiosSupported ? FIVE_TARGETS : THREE_TARGETS_ONSCREEN;
            default:
                throw new IllegalStateException("unrecognized snap mode");
        }