diff options
6 files changed, 96 insertions, 28 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 85921703d559..b29e49a48428 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -118,8 +118,16 @@ public class DividerSnapAlgorithm { mDisplayHeight = displayHeight; mIsLeftRightSplit = isLeftRightSplit; mInsets.set(insets); - mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED : - res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode); + if (Flags.enableFlexibleTwoAppSplit()) { + // In flexible split, we always use a fixed ratio (50%, 66%, or 90%) for splitting + mSnapMode = SNAP_FIXED_RATIO; + } else { + // Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config + mSnapMode = isMinimizedMode + ? SNAP_MODE_MINIMIZED + : res.getInteger( + com.android.internal.R.integer.config_dockedStackDividerSnapMode); + } mFreeSnapMode = res.getBoolean( com.android.internal.R.bool.config_dockedStackDividerFreeSnapMode); mFixedRatio = res.getFraction( @@ -129,8 +137,7 @@ public class DividerSnapAlgorithm { mCalculateRatiosBasedOnAvailableSpace = res.getBoolean( com.android.internal.R.bool.config_flexibleSplitRatios); // If this is a small screen or a foldable, use offscreen ratios - mAllowOffscreenRatios = - Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res); + mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res); mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize( com.android.internal.R.dimen.task_height_of_minimized_mode) : 0; calculateTargets(isLeftRightSplit, dockSide); @@ -299,9 +306,9 @@ public class DividerSnapAlgorithm { private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition, int bottomPosition, int dividerMax) { @PersistentSnapPosition int firstTarget = - mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66; + areOffscreenRatiosSupported() ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66; @PersistentSnapPosition int lastTarget = - mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33; + areOffscreenRatiosSupported() ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33; maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget); addMiddleTarget(isLeftRightSplit); maybeAddTarget(bottomPosition, @@ -313,14 +320,21 @@ public class DividerSnapAlgorithm { int end = isLeftRightSplit ? mDisplayWidth - mInsets.right : mDisplayHeight - mInsets.bottom; - int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; - if (mAllowOffscreenRatios) { - // TODO (b/349828130): This is a placeholder value, real measurements to come - size = (int) (0.3f * (end - start)) - mDividerSize / 2; - } else if (mCalculateRatiosBasedOnAvailableSpace) { - size = Math.max(size, mMinimalSizeResizableTask); + int size; + if (Flags.enableFlexibleTwoAppSplit()) { + float ratio = areOffscreenRatiosSupported() + ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO + : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO; + size = (int) (ratio * (end - start)) - mDividerSize / 2; + } else { + size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; + + if (mCalculateRatiosBasedOnAvailableSpace) { + size = Math.max(size, mMinimalSizeResizableTask); + } } + int topPosition = start + size; int bottomPosition = end - size - mDividerSize; addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); @@ -347,7 +361,7 @@ public class DividerSnapAlgorithm { * meets the minimal size requirement. */ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) { - if (smallerSize >= mMinimalSizeResizableTask || mAllowOffscreenRatios) { + if (smallerSize >= mMinimalSizeResizableTask || areOffscreenRatiosSupported()) { mTargets.add(new SnapTarget(position, snapPosition)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index e7848e27d7ed..cf858deb0327 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -480,6 +480,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mLastDraggingPosition, position, mSplitLayout.FLING_RESIZE_DURATION, + Interpolators.FAST_OUT_SLOW_IN, () -> mSplitLayout.setDividerPosition(position, true /* applyLayoutChange */)); mMoving = false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index f73065ea8eb8..b1e0e9eab5d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -28,6 +28,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE; import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR; import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED; +import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_IN; import static com.android.wm.shell.shared.animation.Interpolators.LINEAR; import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; @@ -77,7 +78,6 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget; import com.android.wm.shell.protolog.ShellProtoLogGroup; -import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition; @@ -100,6 +100,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public static final int FLING_RESIZE_DURATION = 250; private static final int FLING_ENTER_DURATION = 450; private static final int FLING_EXIT_DURATION = 450; + private static final int FLING_OFFSCREEN_DURATION = 500; + + /** A split ratio used on larger screens, where we can fit both apps onscreen. */ + public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f; + /** A split ratio used on smaller screens, where we place one app mostly offscreen. */ + public static final float OFFSCREEN_ASYMMETRIC_RATIO = 0.1f; // Here are some (arbitrarily decided) layer definitions used during animations to make sure the // layers stay in order. Note: This does not affect any other layer numbering systems because @@ -604,25 +610,35 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Sets new divider position and updates bounds correspondingly. Notifies listener if the new * target indicates dismissing split. */ - public void snapToTarget(int currentPosition, SnapTarget snapTarget) { + public void snapToTarget(int currentPosition, SnapTarget snapTarget, int duration, + Interpolator interpolator) { switch (snapTarget.snapPosition) { case SNAP_TO_START_AND_DISMISS: - flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, + flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator, () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */, EXIT_REASON_DRAG_DIVIDER)); break; case SNAP_TO_END_AND_DISMISS: - flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, + flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator, () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */, EXIT_REASON_DRAG_DIVIDER)); break; default: - flingDividerPosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION, + flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator, () -> setDividerPosition(snapTarget.position, true /* applyLayoutChange */)); break; } } + /** + * Same as {@link #snapToTarget(int, SnapTarget)}, with default animation duration and + * interpolator. + */ + public void snapToTarget(int currentPosition, SnapTarget snapTarget) { + snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION, + FAST_OUT_SLOW_IN); + } + void onStartDragging() { mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler, CUJ_SPLIT_SCREEN_RESIZE); @@ -674,14 +690,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void flingDividerToDismiss(boolean toEnd, int reason) { final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position : mDividerSnapAlgorithm.getDismissStartTarget().position; - flingDividerPosition(getDividerPosition(), target, FLING_EXIT_DURATION, + flingDividerPosition(getDividerPosition(), target, FLING_EXIT_DURATION, FAST_OUT_SLOW_IN, () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason)); } /** Fling divider from current position to center position. */ public void flingDividerToCenter(@Nullable Runnable finishCallback) { final int pos = mDividerSnapAlgorithm.getMiddleTarget().position; - flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION, + flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION, FAST_OUT_SLOW_IN, () -> { setDividerPosition(pos, true /* applyLayoutChange */); if (finishCallback != null) { @@ -699,14 +715,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void flingDividerToOtherSide(@PersistentSnapPosition int currentSnapPosition) { switch (currentSnapPosition) { case SNAP_TO_2_10_90 -> - snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget()); + snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget(), + FLING_OFFSCREEN_DURATION, EMPHASIZED); case SNAP_TO_2_90_10 -> - snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget()); + snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget(), + FLING_OFFSCREEN_DURATION, EMPHASIZED); } } @VisibleForTesting - void flingDividerPosition(int from, int to, int duration, + void flingDividerPosition(int from, int to, int duration, Interpolator interpolator, @Nullable Runnable flingFinishedCallback) { if (from == to) { if (flingFinishedCallback != null) { @@ -724,7 +742,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDividerFlingAnimator = ValueAnimator .ofInt(from, to) .setDuration(duration); - mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mDividerFlingAnimator.setInterpolator(interpolator); // If the divider is being physically controlled by the user, we use a cool parallax effect // on the task windows. So if this "snap" animation is an extension of a user-controlled @@ -1048,6 +1066,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return (int) (minWidth / density); } + public int getDisplayWidth() { + return mRootBounds.width(); + } + + public int getDisplayHeight() { + return mRootBounds.height(); + } + /** * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And * restore shifted configuration bounds if it's no longer shifted. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java index 65bf389f3819..9113c0a53178 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java @@ -142,13 +142,25 @@ public class SplitScreenUtils { } /** + * Convenience function for {@link #isLargeScreen(Configuration)}. + */ + public static boolean isLargeScreen(Resources res) { + return isLargeScreen(res.getConfiguration()); + } + + /** + * Returns whether the current device is a foldable + */ + public static boolean isFoldable(Resources res) { + return res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0; + } + + /** * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone * or a foldable (either screen), we allow it. */ public static boolean allowOffscreenRatios(Resources res) { - return !isLargeScreen(res.getConfiguration()) - || - res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0; + return Flags.enableFlexibleTwoAppSplit() && (!isLargeScreen(res) || isFoldable(res)); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 3e76403de51b..fd6f72a0192e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1629,6 +1629,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, leftTopTaskId = mainStageTopTaskId; rightBottomTaskId = sideStageTopTaskId; } + + if (Flags.enableFlexibleTwoAppSplit()) { + // Split screen can be laid out in such a way that some of the apps are offscreen. + // For the purposes of passing SplitBounds up to launcher (for use in thumbnails + // etc.), we crop the bounds down to the screen size. + topLeftBounds.left = + Math.max(topLeftBounds.left, 0); + topLeftBounds.top = + Math.max(topLeftBounds.top, 0); + bottomRightBounds.right = + Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth()); + bottomRightBounds.top = + Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight()); + } + SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds, leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition()); if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index c52d9dd24165..dc0f213338be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -190,7 +190,7 @@ public class SplitLayoutTests extends ShellTestCase { } private void waitDividerFlingFinished() { - verify(mSplitLayout).flingDividerPosition(anyInt(), anyInt(), anyInt(), + verify(mSplitLayout).flingDividerPosition(anyInt(), anyInt(), anyInt(), any(), mRunnableCaptor.capture()); mRunnableCaptor.getValue().run(); } |