Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +108 −16 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.app.ActivityManager; Loading Loading @@ -78,6 +79,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public static final int PARALLAX_DISMISSING = 1; public static final int PARALLAX_ALIGN_CENTER = 2; private static final int FLING_ANIMATION_DURATION = 250; private final int mDividerWindowWidth; private final int mDividerInsets; private final int mDividerSize; Loading @@ -85,7 +88,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final Rect mTempRect = new Rect(); private final Rect mRootBounds = new Rect(); private final Rect mDividerBounds = new Rect(); // Bounds1 final position should be always at top or left private final Rect mBounds1 = new Rect(); // Bounds2 final position should be always at bottom or right private final Rect mBounds2 = new Rect(); private final Rect mWinBounds1 = new Rect(); private final Rect mWinBounds2 = new Rect(); Loading Loading @@ -275,29 +280,36 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange updateBounds(mDividePosition); } /** Updates recording bounds of divider window and both of the splits. */ private void updateBounds(int position) { mDividerBounds.set(mRootBounds); mBounds1.set(mRootBounds); mBounds2.set(mRootBounds); updateBounds(position, mBounds1, mBounds2, mDividerBounds, true /* setEffectBounds */); } /** Updates recording bounds of divider window and both of the splits. */ private void updateBounds(int position, Rect bounds1, Rect bounds2, Rect dividerBounds, boolean setEffectBounds) { dividerBounds.set(mRootBounds); bounds1.set(mRootBounds); bounds2.set(mRootBounds); final boolean isLandscape = isLandscape(mRootBounds); if (isLandscape) { position += mRootBounds.left; mDividerBounds.left = position - mDividerInsets; mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth; mBounds1.right = position; mBounds2.left = mBounds1.right + mDividerSize; dividerBounds.left = position - mDividerInsets; dividerBounds.right = dividerBounds.left + mDividerWindowWidth; bounds1.right = position; bounds2.left = bounds1.right + mDividerSize; } else { position += mRootBounds.top; mDividerBounds.top = position - mDividerInsets; mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth; mBounds1.bottom = position; mBounds2.top = mBounds1.bottom + mDividerSize; } DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */); dividerBounds.top = position - mDividerInsets; dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth; bounds1.bottom = position; bounds2.top = bounds1.bottom + mDividerSize; } DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */); if (setEffectBounds) { mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape); } } /** Inflates {@link DividerView} on the root surface. */ public void init() { Loading Loading @@ -458,7 +470,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } ValueAnimator animator = ValueAnimator .ofInt(from, to) .setDuration(250); .setDuration(FLING_ANIMATION_DURATION); animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); animator.addUpdateListener( animation -> updateDivideBounds((int) animation.getAnimatedValue())); Loading @@ -480,6 +492,86 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange animator.start(); } /** Swich both surface position with animation. */ public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, Runnable finishCallback) { final boolean isLandscape = isLandscape(); final Rect insets = getDisplayInsets(mContext); insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top, isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom); final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( isLandscape ? mBounds2.width() : mBounds2.height()).position; final Rect distBounds1 = new Rect(); final Rect distBounds2 = new Rect(); final Rect distDividerBounds = new Rect(); // Compute dist bounds. updateBounds(dividerPos, distBounds2, distBounds1, distDividerBounds, false /* setEffectBounds */); // Offset to real position under root container. distBounds1.offset(-mRootBounds.left, -mRootBounds.top); distBounds2.offset(-mRootBounds.left, -mRootBounds.top); distDividerBounds.offset(-mRootBounds.left, -mRootBounds.top); // DO NOT move to insets area for smooth animation. distBounds1.set(distBounds1.left, distBounds1.top, distBounds1.right - insets.right, distBounds1.bottom - insets.bottom); distBounds2.set(distBounds2.left + insets.left, distBounds2.top + insets.top, distBounds2.right, distBounds2.bottom); ValueAnimator animator1 = moveSurface(t, leash1, getRefBounds1(), distBounds1, false /* alignStart */); ValueAnimator animator2 = moveSurface(t, leash2, getRefBounds2(), distBounds2, true /* alignStart */); ValueAnimator animator3 = moveSurface(t, getDividerLeash(), getRefDividerBounds(), distDividerBounds, true /* alignStart */); AnimatorSet set = new AnimatorSet(); set.playTogether(animator1, animator2, animator3); set.setDuration(FLING_ANIMATION_DURATION); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDividePosition = dividerPos; updateBounds(mDividePosition); finishCallback.run(); } }); set.start(); } private ValueAnimator moveSurface(SurfaceControl.Transaction t, SurfaceControl leash, Rect start, Rect end, boolean alignStart) { Rect tempStart = new Rect(start); Rect tempEnd = new Rect(end); final float diffX = tempEnd.left - tempStart.left; final float diffY = tempEnd.top - tempStart.top; final float diffWidth = tempEnd.width() - tempStart.width(); final float diffHeight = tempEnd.height() - tempStart.height(); ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.addUpdateListener(animation -> { if (leash == null) return; final float scale = (float) animation.getAnimatedValue(); final float distX = tempStart.left + scale * diffX; final float distY = tempStart.top + scale * diffY; final int width = (int) (tempStart.width() + scale * diffWidth); final int height = (int) (tempStart.height() + scale * diffHeight); if (alignStart) { t.setPosition(leash, distX, distY); t.setWindowCrop(leash, width, height); } else { final int offsetX = width - start.width(); final int offsetY = height - start.height(); t.setPosition(leash, distX + offsetX, distY + offsetY); mTempRect.set(0, 0, width, height); mTempRect.offsetTo(-offsetX, -offsetY); t.setCrop(leash, mTempRect); } t.apply(); }); return animator; } private static Rect getDisplayInsets(Context context) { return context.getSystemService(WindowManager.class) .getMaximumWindowMetrics() Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +16 −1 Original line number Diff line number Diff line Loading @@ -611,6 +611,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) { if (mSideStagePosition == sideStagePosition) return; SurfaceControl.Transaction t = mTransactionPool.acquire(); final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash, () -> { setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */); mTransactionPool.release(t); }); } void setSideStagePosition(@SplitPosition int sideStagePosition, @Nullable WindowContainerTransaction wct) { setSideStagePosition(sideStagePosition, true /* updateBounds */, wct); Loading Loading @@ -1209,7 +1224,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onDoubleTappedDivider() { setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */); setSideStagePositionAnimated(SplitLayout.reversePosition(mSideStagePosition)); mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), getSideStagePosition(), mSideStage.getTopChildTaskUid(), mSplitLayout.isLandscape()); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +108 −16 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.app.ActivityManager; Loading Loading @@ -78,6 +79,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public static final int PARALLAX_DISMISSING = 1; public static final int PARALLAX_ALIGN_CENTER = 2; private static final int FLING_ANIMATION_DURATION = 250; private final int mDividerWindowWidth; private final int mDividerInsets; private final int mDividerSize; Loading @@ -85,7 +88,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final Rect mTempRect = new Rect(); private final Rect mRootBounds = new Rect(); private final Rect mDividerBounds = new Rect(); // Bounds1 final position should be always at top or left private final Rect mBounds1 = new Rect(); // Bounds2 final position should be always at bottom or right private final Rect mBounds2 = new Rect(); private final Rect mWinBounds1 = new Rect(); private final Rect mWinBounds2 = new Rect(); Loading Loading @@ -275,29 +280,36 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange updateBounds(mDividePosition); } /** Updates recording bounds of divider window and both of the splits. */ private void updateBounds(int position) { mDividerBounds.set(mRootBounds); mBounds1.set(mRootBounds); mBounds2.set(mRootBounds); updateBounds(position, mBounds1, mBounds2, mDividerBounds, true /* setEffectBounds */); } /** Updates recording bounds of divider window and both of the splits. */ private void updateBounds(int position, Rect bounds1, Rect bounds2, Rect dividerBounds, boolean setEffectBounds) { dividerBounds.set(mRootBounds); bounds1.set(mRootBounds); bounds2.set(mRootBounds); final boolean isLandscape = isLandscape(mRootBounds); if (isLandscape) { position += mRootBounds.left; mDividerBounds.left = position - mDividerInsets; mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth; mBounds1.right = position; mBounds2.left = mBounds1.right + mDividerSize; dividerBounds.left = position - mDividerInsets; dividerBounds.right = dividerBounds.left + mDividerWindowWidth; bounds1.right = position; bounds2.left = bounds1.right + mDividerSize; } else { position += mRootBounds.top; mDividerBounds.top = position - mDividerInsets; mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth; mBounds1.bottom = position; mBounds2.top = mBounds1.bottom + mDividerSize; } DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */); dividerBounds.top = position - mDividerInsets; dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth; bounds1.bottom = position; bounds2.top = bounds1.bottom + mDividerSize; } DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */); if (setEffectBounds) { mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape); } } /** Inflates {@link DividerView} on the root surface. */ public void init() { Loading Loading @@ -458,7 +470,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } ValueAnimator animator = ValueAnimator .ofInt(from, to) .setDuration(250); .setDuration(FLING_ANIMATION_DURATION); animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); animator.addUpdateListener( animation -> updateDivideBounds((int) animation.getAnimatedValue())); Loading @@ -480,6 +492,86 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange animator.start(); } /** Swich both surface position with animation. */ public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, Runnable finishCallback) { final boolean isLandscape = isLandscape(); final Rect insets = getDisplayInsets(mContext); insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top, isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom); final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( isLandscape ? mBounds2.width() : mBounds2.height()).position; final Rect distBounds1 = new Rect(); final Rect distBounds2 = new Rect(); final Rect distDividerBounds = new Rect(); // Compute dist bounds. updateBounds(dividerPos, distBounds2, distBounds1, distDividerBounds, false /* setEffectBounds */); // Offset to real position under root container. distBounds1.offset(-mRootBounds.left, -mRootBounds.top); distBounds2.offset(-mRootBounds.left, -mRootBounds.top); distDividerBounds.offset(-mRootBounds.left, -mRootBounds.top); // DO NOT move to insets area for smooth animation. distBounds1.set(distBounds1.left, distBounds1.top, distBounds1.right - insets.right, distBounds1.bottom - insets.bottom); distBounds2.set(distBounds2.left + insets.left, distBounds2.top + insets.top, distBounds2.right, distBounds2.bottom); ValueAnimator animator1 = moveSurface(t, leash1, getRefBounds1(), distBounds1, false /* alignStart */); ValueAnimator animator2 = moveSurface(t, leash2, getRefBounds2(), distBounds2, true /* alignStart */); ValueAnimator animator3 = moveSurface(t, getDividerLeash(), getRefDividerBounds(), distDividerBounds, true /* alignStart */); AnimatorSet set = new AnimatorSet(); set.playTogether(animator1, animator2, animator3); set.setDuration(FLING_ANIMATION_DURATION); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDividePosition = dividerPos; updateBounds(mDividePosition); finishCallback.run(); } }); set.start(); } private ValueAnimator moveSurface(SurfaceControl.Transaction t, SurfaceControl leash, Rect start, Rect end, boolean alignStart) { Rect tempStart = new Rect(start); Rect tempEnd = new Rect(end); final float diffX = tempEnd.left - tempStart.left; final float diffY = tempEnd.top - tempStart.top; final float diffWidth = tempEnd.width() - tempStart.width(); final float diffHeight = tempEnd.height() - tempStart.height(); ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.addUpdateListener(animation -> { if (leash == null) return; final float scale = (float) animation.getAnimatedValue(); final float distX = tempStart.left + scale * diffX; final float distY = tempStart.top + scale * diffY; final int width = (int) (tempStart.width() + scale * diffWidth); final int height = (int) (tempStart.height() + scale * diffHeight); if (alignStart) { t.setPosition(leash, distX, distY); t.setWindowCrop(leash, width, height); } else { final int offsetX = width - start.width(); final int offsetY = height - start.height(); t.setPosition(leash, distX + offsetX, distY + offsetY); mTempRect.set(0, 0, width, height); mTempRect.offsetTo(-offsetX, -offsetY); t.setCrop(leash, mTempRect); } t.apply(); }); return animator; } private static Rect getDisplayInsets(Context context) { return context.getSystemService(WindowManager.class) .getMaximumWindowMetrics() Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +16 −1 Original line number Diff line number Diff line Loading @@ -611,6 +611,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) { if (mSideStagePosition == sideStagePosition) return; SurfaceControl.Transaction t = mTransactionPool.acquire(); final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash, () -> { setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */); mTransactionPool.release(t); }); } void setSideStagePosition(@SplitPosition int sideStagePosition, @Nullable WindowContainerTransaction wct) { setSideStagePosition(sideStagePosition, true /* updateBounds */, wct); Loading Loading @@ -1209,7 +1224,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onDoubleTappedDivider() { setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */); setSideStagePositionAnimated(SplitLayout.reversePosition(mSideStagePosition)); mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), getSideStagePosition(), mSideStage.getTopChildTaskUid(), mSplitLayout.isLandscape()); Loading