Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +55 −0 Original line number Original line Diff line number Diff line Loading @@ -26,10 +26,14 @@ import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.IntDef; import android.app.TaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Rect; import android.graphics.Rect; import android.view.Choreographer; import android.view.Choreographer; import android.view.Surface; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.view.SurfaceSession; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; Loading Loading @@ -253,6 +257,7 @@ public class PipAnimationController { mSurfaceControlTransactionFactory; mSurfaceControlTransactionFactory; private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private @TransitionDirection int mTransitionDirection; private @TransitionDirection int mTransitionDirection; protected SurfaceControl mContentOverlay; private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, Loading Loading @@ -331,6 +336,53 @@ public class PipAnimationController { return false; return false; } } SurfaceControl getContentOverlay() { return mContentOverlay; } PipTransitionAnimator<T> setUseContentOverlay(Context context) { final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); if (mContentOverlay != null) { // remove existing content overlay if there is any. tx.remove(mContentOverlay); tx.apply(); } mContentOverlay = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName("PipContentOverlay") .setColorLayer() .build(); tx.show(mContentOverlay); tx.setLayer(mContentOverlay, Integer.MAX_VALUE); tx.setColor(mContentOverlay, getContentOverlayColor(context)); tx.setAlpha(mContentOverlay, 0f); tx.reparent(mContentOverlay, mLeash); tx.apply(); return this; } private float[] getContentOverlayColor(Context context) { final TypedArray ta = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackground }); try { int colorAccent = ta.getColor(0, 0); return new float[] { Color.red(colorAccent) / 255f, Color.green(colorAccent) / 255f, Color.blue(colorAccent) / 255f }; } finally { ta.recycle(); } } /** * Clears the {@link #mContentOverlay}, this should be done after the content overlay is * faded out, such as in {@link PipTaskOrganizer#fadeOutAndRemoveOverlay} */ void clearContentOverlay() { mContentOverlay = null; } @VisibleForTesting @VisibleForTesting @TransitionDirection public int getTransitionDirection() { @TransitionDirection public int getTransitionDirection() { return mTransitionDirection; return mTransitionDirection; Loading Loading @@ -517,6 +569,9 @@ public class PipAnimationController { final Rect base = getBaseValue(); final Rect base = getBaseValue(); final Rect start = getStartValue(); final Rect start = getStartValue(); final Rect end = getEndValue(); final Rect end = getEndValue(); if (mContentOverlay != null) { tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } if (rotatedEndRect != null) { if (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when // Animate the bounds in a different orientation. It only happens when // switching between PiP and fullscreen. // switching between PiP and fullscreen. Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +54 −18 Original line number Original line Diff line number Diff line Loading @@ -107,6 +107,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ */ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; /** * The fixed start delay in ms when fading out the content overlay from bounds animation. * This is to overcome the flicker caused by configuration change when rotating from landscape * to portrait PiP in button navigation mode. */ private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500; private final Context mContext; private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; private final PipBoundsState mPipBoundsState; Loading Loading @@ -144,6 +151,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final int direction = animator.getTransitionDirection(); final int direction = animator.getTransitionDirection(); final int animationType = animator.getAnimationType(); final int animationType = animator.getAnimationType(); final Rect destinationBounds = animator.getDestinationBounds(); final Rect destinationBounds = animator.getDestinationBounds(); if (isInPipDirection(direction) && animator.getContentOverlay() != null) { fadeOutAndRemoveOverlay(animator.getContentOverlay(), animator::clearContentOverlay, true /* withStartDelay*/); } if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS && direction == TRANSITION_DIRECTION_TO_PIP) { && direction == TRANSITION_DIRECTION_TO_PIP) { // Notify the display to continue the deferred orientation change. // Notify the display to continue the deferred orientation change. Loading @@ -168,17 +179,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, finishResize(tx, destinationBounds, direction, animationType); finishResize(tx, destinationBounds, direction, animationType); sendOnPipTransitionFinished(direction); sendOnPipTransitionFinished(direction); } } if (direction == TRANSITION_DIRECTION_TO_PIP) { // TODO (b//169221267): Add jank listener for transactions without buffer updates. //InteractionJankMonitor.getInstance().end( // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP); } } } @Override @Override public void onPipAnimationCancel(TaskInfo taskInfo, public void onPipAnimationCancel(TaskInfo taskInfo, PipAnimationController.PipTransitionAnimator animator) { PipAnimationController.PipTransitionAnimator animator) { sendOnPipTransitionCancelled(animator.getTransitionDirection()); final int direction = animator.getTransitionDirection(); if (isInPipDirection(direction) && animator.getContentOverlay() != null) { fadeOutAndRemoveOverlay(animator.getContentOverlay(), animator::clearContentOverlay, true /* withStartDelay */); } sendOnPipTransitionCancelled(direction); } } }; }; Loading Loading @@ -635,7 +646,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // Remove the swipe to home overlay // Remove the swipe to home overlay if (swipeToHomeOverlay != null) { if (swipeToHomeOverlay != null) { fadeOutAndRemoveOverlay(swipeToHomeOverlay); fadeOutAndRemoveOverlay(swipeToHomeOverlay, null /* callback */, false /* withStartDelay */); } } }, tx); }, tx); mInSwipePipToHomeTransition = false; mInSwipePipToHomeTransition = false; Loading Loading @@ -718,9 +730,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } } final PipAnimationController.PipTransitionAnimator animator = final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator(); if (animator != null) { if (animator != null) { if (animator.getContentOverlay() != null) { removeContentOverlay(animator.getContentOverlay(), animator::clearContentOverlay); } animator.removeAllUpdateListeners(); animator.removeAllUpdateListeners(); animator.removeAllListeners(); animator.removeAllListeners(); animator.cancel(); animator.cancel(); Loading Loading @@ -1195,7 +1210,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, snapshotDest); snapshotDest); // Start animation to fade out the snapshot. // Start animation to fade out the snapshot. fadeOutAndRemoveOverlay(snapshotSurface); fadeOutAndRemoveOverlay(snapshotSurface, null /* callback */, false /* withStartDelay */); }); }); } else { } else { applyFinishBoundsResize(wct, direction); applyFinishBoundsResize(wct, direction); Loading Loading @@ -1286,15 +1302,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setTransitionDirection(direction) animator.setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setPipTransactionHandler(mPipTransactionHandler) .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs) .setDuration(durationMs); .start(); if (isInPipDirection(direction)) { if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) { // Similar to auto-enter-pip transition, we use content overlay when there is no // source rect hint to enter PiP use bounds animation. if (sourceHintRect == null) animator.setUseContentOverlay(mContext); // The destination bounds are used for the end rect of animation and the final bounds // The destination bounds are used for the end rect of animation and the final bounds // after animation finishes. So after the animation is started, the destination bounds // after animation finishes. So after the animation is started, the destination bounds // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout // without affecting the animation. // without affecting the animation. if (rotationDelta != Surface.ROTATION_0) { animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); } } } animator.start(); return animator; return animator; } } Loading @@ -1307,6 +1328,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); // Transform the destination bounds to current display coordinates. // Transform the destination bounds to current display coordinates. rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation); rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation); // When entering PiP (from button navigation mode), adjust the source rect hint by // display cutout if applicable. if (sourceHintRect != null && mTaskInfo.displayCutoutInsets != null) { if (rotationDelta == Surface.ROTATION_270) { sourceHintRect.offset(mTaskInfo.displayCutoutInsets.left, mTaskInfo.displayCutoutInsets.top); } } } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), Loading Loading @@ -1345,7 +1374,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** /** * Fades out and removes an overlay surface. * Fades out and removes an overlay surface. */ */ private void fadeOutAndRemoveOverlay(SurfaceControl surface) { private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback, boolean withStartDelay) { if (surface == null) { if (surface == null) { return; return; } } Loading @@ -1362,13 +1392,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.addListener(new AnimatorListenerAdapter() { animator.addListener(new AnimatorListenerAdapter() { @Override @Override public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) { removeContentOverlay(surface, callback); } }); animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0); animator.start(); } private void removeContentOverlay(SurfaceControl surface, Runnable callback) { final SurfaceControl.Transaction tx = final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceControlTransactionFactory.getTransaction(); tx.remove(surface); tx.remove(surface); tx.apply(); tx.apply(); } if (callback != null) callback.run(); }); animator.start(); } } /** /** Loading services/core/java/com/android/server/wm/DisplayContent.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1901,7 +1901,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllWindows(w -> { forAllWindows(w -> { w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); }, true /* traverseTopToBottom */); }, true /* traverseTopToBottom */); mPinnedTaskController.startSeamlessRotationIfNeeded(transaction); mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation); } } mWmService.mDisplayManagerInternal.performTraversal(transaction); mWmService.mDisplayManagerInternal.performTraversal(transaction); Loading services/core/java/com/android/server/wm/PinnedTaskController.java +15 −1 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,16 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import android.util.Log; import android.util.RotationUtils; import android.util.Slog; import android.util.Slog; import android.view.IPinnedTaskListener; import android.view.IPinnedTaskListener; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.PictureInPictureSurfaceTransaction; import android.window.PictureInPictureSurfaceTransaction; Loading Loading @@ -237,7 +240,8 @@ class PinnedTaskController { * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it * receives the callback of fixed rotation completion. * receives the callback of fixed rotation completion. */ */ void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t) { void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t, int oldRotation, int newRotation) { final Rect bounds = mDestRotatedBounds; final Rect bounds = mDestRotatedBounds; final PictureInPictureSurfaceTransaction pipTx = mPipTransaction; final PictureInPictureSurfaceTransaction pipTx = mPipTransaction; if (bounds == null && pipTx == null) { if (bounds == null && pipTx == null) { Loading Loading @@ -280,6 +284,16 @@ class PinnedTaskController { ? params.getSourceRectHint() ? params.getSourceRectHint() : null; : null; Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect); Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect); final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation); // Adjust for display cutout if applicable. if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) { if (pinnedTask.getDisplayCutoutInsets() != null) { final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation); final Rect displayCutoutInsets = RotationUtils.rotateInsets( Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect(); sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top); } } final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect) final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect) ? sourceHintRect : areaBounds; ? sourceHintRect : areaBounds; final int w = contentBounds.width(); final int w = contentBounds.width(); Loading services/core/java/com/android/server/wm/Task.java +5 −6 Original line number Original line Diff line number Diff line Loading @@ -3341,7 +3341,7 @@ class Task extends TaskFragment { info.positionInParent = getRelativePosition(); info.positionInParent = getRelativePosition(); info.pictureInPictureParams = getPictureInPictureParams(top); info.pictureInPictureParams = getPictureInPictureParams(top); info.displayCutoutInsets = getDisplayCutoutInsets(top); info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null; info.topActivityInfo = mReuseActivitiesReport.top != null info.topActivityInfo = mReuseActivitiesReport.top != null ? mReuseActivitiesReport.top.info ? mReuseActivitiesReport.top.info : null; : null; Loading Loading @@ -3377,16 +3377,15 @@ class Task extends TaskFragment { ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs); ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs); } } private Rect getDisplayCutoutInsets(Task top) { Rect getDisplayCutoutInsets() { if (top == null || top.mDisplayContent == null if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null; || top.getDisplayInfo().displayCutout == null) return null; final WindowState w = getTopVisibleAppMainWindow(); final WindowState w = top.getTopVisibleAppMainWindow(); final int displayCutoutMode = w == null final int displayCutoutMode = w == null ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT : w.getAttrs().layoutInDisplayCutoutMode; : w.getAttrs().layoutInDisplayCutoutMode; return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) ? null : top.getDisplayInfo().displayCutout.getSafeInsets(); ? null : getDisplayInfo().displayCutout.getSafeInsets(); } } /** /** Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +55 −0 Original line number Original line Diff line number Diff line Loading @@ -26,10 +26,14 @@ import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.IntDef; import android.app.TaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Rect; import android.graphics.Rect; import android.view.Choreographer; import android.view.Choreographer; import android.view.Surface; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.view.SurfaceSession; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; Loading Loading @@ -253,6 +257,7 @@ public class PipAnimationController { mSurfaceControlTransactionFactory; mSurfaceControlTransactionFactory; private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private @TransitionDirection int mTransitionDirection; private @TransitionDirection int mTransitionDirection; protected SurfaceControl mContentOverlay; private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, Loading Loading @@ -331,6 +336,53 @@ public class PipAnimationController { return false; return false; } } SurfaceControl getContentOverlay() { return mContentOverlay; } PipTransitionAnimator<T> setUseContentOverlay(Context context) { final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); if (mContentOverlay != null) { // remove existing content overlay if there is any. tx.remove(mContentOverlay); tx.apply(); } mContentOverlay = new SurfaceControl.Builder(new SurfaceSession()) .setCallsite("PipAnimation") .setName("PipContentOverlay") .setColorLayer() .build(); tx.show(mContentOverlay); tx.setLayer(mContentOverlay, Integer.MAX_VALUE); tx.setColor(mContentOverlay, getContentOverlayColor(context)); tx.setAlpha(mContentOverlay, 0f); tx.reparent(mContentOverlay, mLeash); tx.apply(); return this; } private float[] getContentOverlayColor(Context context) { final TypedArray ta = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackground }); try { int colorAccent = ta.getColor(0, 0); return new float[] { Color.red(colorAccent) / 255f, Color.green(colorAccent) / 255f, Color.blue(colorAccent) / 255f }; } finally { ta.recycle(); } } /** * Clears the {@link #mContentOverlay}, this should be done after the content overlay is * faded out, such as in {@link PipTaskOrganizer#fadeOutAndRemoveOverlay} */ void clearContentOverlay() { mContentOverlay = null; } @VisibleForTesting @VisibleForTesting @TransitionDirection public int getTransitionDirection() { @TransitionDirection public int getTransitionDirection() { return mTransitionDirection; return mTransitionDirection; Loading Loading @@ -517,6 +569,9 @@ public class PipAnimationController { final Rect base = getBaseValue(); final Rect base = getBaseValue(); final Rect start = getStartValue(); final Rect start = getStartValue(); final Rect end = getEndValue(); final Rect end = getEndValue(); if (mContentOverlay != null) { tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } if (rotatedEndRect != null) { if (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when // Animate the bounds in a different orientation. It only happens when // switching between PiP and fullscreen. // switching between PiP and fullscreen. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +54 −18 Original line number Original line Diff line number Diff line Loading @@ -107,6 +107,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ */ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; /** * The fixed start delay in ms when fading out the content overlay from bounds animation. * This is to overcome the flicker caused by configuration change when rotating from landscape * to portrait PiP in button navigation mode. */ private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500; private final Context mContext; private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; private final PipBoundsState mPipBoundsState; Loading Loading @@ -144,6 +151,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final int direction = animator.getTransitionDirection(); final int direction = animator.getTransitionDirection(); final int animationType = animator.getAnimationType(); final int animationType = animator.getAnimationType(); final Rect destinationBounds = animator.getDestinationBounds(); final Rect destinationBounds = animator.getDestinationBounds(); if (isInPipDirection(direction) && animator.getContentOverlay() != null) { fadeOutAndRemoveOverlay(animator.getContentOverlay(), animator::clearContentOverlay, true /* withStartDelay*/); } if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS && direction == TRANSITION_DIRECTION_TO_PIP) { && direction == TRANSITION_DIRECTION_TO_PIP) { // Notify the display to continue the deferred orientation change. // Notify the display to continue the deferred orientation change. Loading @@ -168,17 +179,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, finishResize(tx, destinationBounds, direction, animationType); finishResize(tx, destinationBounds, direction, animationType); sendOnPipTransitionFinished(direction); sendOnPipTransitionFinished(direction); } } if (direction == TRANSITION_DIRECTION_TO_PIP) { // TODO (b//169221267): Add jank listener for transactions without buffer updates. //InteractionJankMonitor.getInstance().end( // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP); } } } @Override @Override public void onPipAnimationCancel(TaskInfo taskInfo, public void onPipAnimationCancel(TaskInfo taskInfo, PipAnimationController.PipTransitionAnimator animator) { PipAnimationController.PipTransitionAnimator animator) { sendOnPipTransitionCancelled(animator.getTransitionDirection()); final int direction = animator.getTransitionDirection(); if (isInPipDirection(direction) && animator.getContentOverlay() != null) { fadeOutAndRemoveOverlay(animator.getContentOverlay(), animator::clearContentOverlay, true /* withStartDelay */); } sendOnPipTransitionCancelled(direction); } } }; }; Loading Loading @@ -635,7 +646,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // Remove the swipe to home overlay // Remove the swipe to home overlay if (swipeToHomeOverlay != null) { if (swipeToHomeOverlay != null) { fadeOutAndRemoveOverlay(swipeToHomeOverlay); fadeOutAndRemoveOverlay(swipeToHomeOverlay, null /* callback */, false /* withStartDelay */); } } }, tx); }, tx); mInSwipePipToHomeTransition = false; mInSwipePipToHomeTransition = false; Loading Loading @@ -718,9 +730,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } } final PipAnimationController.PipTransitionAnimator animator = final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator(); if (animator != null) { if (animator != null) { if (animator.getContentOverlay() != null) { removeContentOverlay(animator.getContentOverlay(), animator::clearContentOverlay); } animator.removeAllUpdateListeners(); animator.removeAllUpdateListeners(); animator.removeAllListeners(); animator.removeAllListeners(); animator.cancel(); animator.cancel(); Loading Loading @@ -1195,7 +1210,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, snapshotDest); snapshotDest); // Start animation to fade out the snapshot. // Start animation to fade out the snapshot. fadeOutAndRemoveOverlay(snapshotSurface); fadeOutAndRemoveOverlay(snapshotSurface, null /* callback */, false /* withStartDelay */); }); }); } else { } else { applyFinishBoundsResize(wct, direction); applyFinishBoundsResize(wct, direction); Loading Loading @@ -1286,15 +1302,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setTransitionDirection(direction) animator.setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setPipTransactionHandler(mPipTransactionHandler) .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs) .setDuration(durationMs); .start(); if (isInPipDirection(direction)) { if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) { // Similar to auto-enter-pip transition, we use content overlay when there is no // source rect hint to enter PiP use bounds animation. if (sourceHintRect == null) animator.setUseContentOverlay(mContext); // The destination bounds are used for the end rect of animation and the final bounds // The destination bounds are used for the end rect of animation and the final bounds // after animation finishes. So after the animation is started, the destination bounds // after animation finishes. So after the animation is started, the destination bounds // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout // without affecting the animation. // without affecting the animation. if (rotationDelta != Surface.ROTATION_0) { animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); } } } animator.start(); return animator; return animator; } } Loading @@ -1307,6 +1328,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); // Transform the destination bounds to current display coordinates. // Transform the destination bounds to current display coordinates. rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation); rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation); // When entering PiP (from button navigation mode), adjust the source rect hint by // display cutout if applicable. if (sourceHintRect != null && mTaskInfo.displayCutoutInsets != null) { if (rotationDelta == Surface.ROTATION_270) { sourceHintRect.offset(mTaskInfo.displayCutoutInsets.left, mTaskInfo.displayCutoutInsets.top); } } } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), Loading Loading @@ -1345,7 +1374,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** /** * Fades out and removes an overlay surface. * Fades out and removes an overlay surface. */ */ private void fadeOutAndRemoveOverlay(SurfaceControl surface) { private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback, boolean withStartDelay) { if (surface == null) { if (surface == null) { return; return; } } Loading @@ -1362,13 +1392,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.addListener(new AnimatorListenerAdapter() { animator.addListener(new AnimatorListenerAdapter() { @Override @Override public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) { removeContentOverlay(surface, callback); } }); animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0); animator.start(); } private void removeContentOverlay(SurfaceControl surface, Runnable callback) { final SurfaceControl.Transaction tx = final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceControlTransactionFactory.getTransaction(); tx.remove(surface); tx.remove(surface); tx.apply(); tx.apply(); } if (callback != null) callback.run(); }); animator.start(); } } /** /** Loading
services/core/java/com/android/server/wm/DisplayContent.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1901,7 +1901,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllWindows(w -> { forAllWindows(w -> { w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); }, true /* traverseTopToBottom */); }, true /* traverseTopToBottom */); mPinnedTaskController.startSeamlessRotationIfNeeded(transaction); mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation); } } mWmService.mDisplayManagerInternal.performTraversal(transaction); mWmService.mDisplayManagerInternal.performTraversal(transaction); Loading
services/core/java/com/android/server/wm/PinnedTaskController.java +15 −1 Original line number Original line Diff line number Diff line Loading @@ -27,13 +27,16 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import android.util.Log; import android.util.RotationUtils; import android.util.Slog; import android.util.Slog; import android.view.IPinnedTaskListener; import android.view.IPinnedTaskListener; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.PictureInPictureSurfaceTransaction; import android.window.PictureInPictureSurfaceTransaction; Loading Loading @@ -237,7 +240,8 @@ class PinnedTaskController { * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it * receives the callback of fixed rotation completion. * receives the callback of fixed rotation completion. */ */ void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t) { void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t, int oldRotation, int newRotation) { final Rect bounds = mDestRotatedBounds; final Rect bounds = mDestRotatedBounds; final PictureInPictureSurfaceTransaction pipTx = mPipTransaction; final PictureInPictureSurfaceTransaction pipTx = mPipTransaction; if (bounds == null && pipTx == null) { if (bounds == null && pipTx == null) { Loading Loading @@ -280,6 +284,16 @@ class PinnedTaskController { ? params.getSourceRectHint() ? params.getSourceRectHint() : null; : null; Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect); Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect); final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation); // Adjust for display cutout if applicable. if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) { if (pinnedTask.getDisplayCutoutInsets() != null) { final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation); final Rect displayCutoutInsets = RotationUtils.rotateInsets( Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect(); sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top); } } final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect) final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect) ? sourceHintRect : areaBounds; ? sourceHintRect : areaBounds; final int w = contentBounds.width(); final int w = contentBounds.width(); Loading
services/core/java/com/android/server/wm/Task.java +5 −6 Original line number Original line Diff line number Diff line Loading @@ -3341,7 +3341,7 @@ class Task extends TaskFragment { info.positionInParent = getRelativePosition(); info.positionInParent = getRelativePosition(); info.pictureInPictureParams = getPictureInPictureParams(top); info.pictureInPictureParams = getPictureInPictureParams(top); info.displayCutoutInsets = getDisplayCutoutInsets(top); info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null; info.topActivityInfo = mReuseActivitiesReport.top != null info.topActivityInfo = mReuseActivitiesReport.top != null ? mReuseActivitiesReport.top.info ? mReuseActivitiesReport.top.info : null; : null; Loading Loading @@ -3377,16 +3377,15 @@ class Task extends TaskFragment { ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs); ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs); } } private Rect getDisplayCutoutInsets(Task top) { Rect getDisplayCutoutInsets() { if (top == null || top.mDisplayContent == null if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null; || top.getDisplayInfo().displayCutout == null) return null; final WindowState w = getTopVisibleAppMainWindow(); final WindowState w = top.getTopVisibleAppMainWindow(); final int displayCutoutMode = w == null final int displayCutoutMode = w == null ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT : w.getAttrs().layoutInDisplayCutoutMode; : w.getAttrs().layoutInDisplayCutoutMode; return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) ? null : top.getDisplayInfo().displayCutout.getSafeInsets(); ? null : getDisplayInfo().displayCutout.getSafeInsets(); } } /** /** Loading