Loading core/java/android/app/ActivityThread.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -5685,6 +5685,13 @@ public final class ActivityThread extends ClientTransactionHandler { final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull( final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull( amOverrideConfig, contextThemeWrapperOverrideConfig); amOverrideConfig, contextThemeWrapperOverrideConfig); mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); final Resources res = activity.getResources(); if (res.hasOverrideDisplayAdjustments()) { // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated // configuration of activity may be sent later than the adjustments. In this case, the // adjustments need to be updated for the consistency of display info. res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig); } activity.mConfigChangeFlags = 0; activity.mConfigChangeFlags = 0; activity.mCurrentConfig = new Configuration(newConfig); activity.mCurrentConfig = new Configuration(newConfig); Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +51 −4 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.wm.shell.pip; package com.android.wm.shell.pip; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.animation.AnimationHandler; import android.animation.AnimationHandler; import android.animation.Animator; import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.RectEvaluator; Loading @@ -24,11 +27,13 @@ import android.annotation.IntDef; import android.app.TaskInfo; import android.app.TaskInfo; import android.graphics.Rect; import android.graphics.Rect; import android.view.Choreographer; import android.view.Choreographer; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.DisplayLayout; import java.lang.annotation.Retention; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -132,15 +137,21 @@ public class PipAnimationController { * leash bounds before transformation/any animation. This is so when we try to construct * leash bounds before transformation/any animation. This is so when we try to construct * the different transformation matrices for the animation, we are constructing this based off * the different transformation matrices for the animation, we are constructing this based off * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. * * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the * rotation change. */ */ @VisibleForTesting @VisibleForTesting public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { if (mCurrentAnimator == null) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, endBounds, sourceHintRect, direction, 0 /* startingAngle */)); endBounds, sourceHintRect, direction, 0 /* startingAngle */, rotationDelta)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure // If we are still animating the fade into pip, then just move the surface and ensure Loading @@ -156,7 +167,7 @@ public class PipAnimationController { mCurrentAnimator.cancel(); mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, endBounds, sourceHintRect, direction, startingAngle)); endBounds, sourceHintRect, direction, startingAngle, rotationDelta)); } } return mCurrentAnimator; return mCurrentAnimator; } } Loading Loading @@ -410,7 +421,8 @@ public class PipAnimationController { static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { // Just for simplicity we'll interpolate between the source rect hint insets and empty // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop // insets to calculate the window crop final Rect initialSourceValue; final Rect initialSourceValue; Loading @@ -431,6 +443,16 @@ public class PipAnimationController { } } final Rect sourceInsets = new Rect(0, 0, 0, 0); final Rect sourceInsets = new Rect(0, 0, 0, 0); final Rect rotatedEndRect; if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { // Rotate the end bounds according to the rotation delta because the display will // be rotated to the same orientation. rotatedEndRect = new Rect(endValue); DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta); } else { rotatedEndRect = null; } // construct new Rect instances in case they are recycled // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), Loading @@ -444,6 +466,12 @@ 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 (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when // leaving PiP to fullscreen. applyRotation(tx, leash, fraction, start, end, rotatedEndRect); return; } Rect bounds = mRectEvaluator.evaluate(fraction, start, end); Rect bounds = mRectEvaluator.evaluate(fraction, start, end); float angle = (1.0f - fraction) * startingAngle; float angle = (1.0f - fraction) * startingAngle; setCurrentValue(bounds); setCurrentValue(bounds); Loading @@ -469,6 +497,25 @@ public class PipAnimationController { tx.apply(); tx.apply(); } } private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash, float fraction, Rect start, Rect end, Rect rotatedEndRect) { final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect); setCurrentValue(bounds); final float degree, x, y; if (rotationDelta == ROTATION_90) { degree = 90 * fraction; x = fraction * (end.right - start.left) + start.left; y = fraction * (end.top - start.top) + start.top; } else { degree = -90 * fraction; x = fraction * (end.left - start.left) + start.left; y = fraction * (end.bottom - start.top) + start.top; } getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds, rotatedEndRect, degree, x, y); tx.apply(); } @Override @Override void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { getSurfaceTransactionHelper() getSurfaceTransactionHelper() Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +26 −0 Original line number Original line Diff line number Diff line Loading @@ -126,6 +126,32 @@ public class PipSurfaceTransactionHelper { return this; return this; } } /** * Operates the rotation according to the given degrees and scale (setMatrix) according to the * source bounds and rotated destination bounds. The crop will be the unscaled source bounds. * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees, float positionX, float positionY) { mTmpDestinationRect.set(sourceBounds); final int dw = destinationBounds.width(); final int dh = destinationBounds.height(); // Scale by the short side so there won't be empty area if the aspect ratio of source and // destination are different. final float scale = dw <= dh ? (float) sourceBounds.width() / dw : (float) sourceBounds.height() / dh; // Inverse scale for crop to fit in screen coordinates. mTmpDestinationRect.scale(1 / scale); mTmpTransform.setRotate(degrees); mTmpTransform.postScale(scale, scale); mTmpTransform.postTranslate(positionX, positionY); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height()); return this; } /** /** * Resets the scale (setMatrix) on a given transaction and leash if there's any * Resets the scale (setMatrix) on a given transaction and leash if there's any * * Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +41 −63 Original line number Original line Diff line number Diff line Loading @@ -48,13 +48,12 @@ import android.app.TaskInfo; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import android.util.Log; import android.util.Rational; import android.util.Rational; import android.view.Display; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerToken; Loading @@ -72,8 +71,6 @@ import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Objects; import java.util.Optional; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Consumer; Loading Loading @@ -134,7 +131,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipUiEventLogger mPipUiEventLoggerLogger; private final PipUiEventLogger mPipUiEventLoggerLogger; private final int mEnterExitAnimationDuration; private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<LegacySplitScreen> mSplitScreenOptional; private final Optional<LegacySplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; protected final ShellExecutor mMainExecutor; Loading Loading @@ -191,6 +187,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ */ private boolean mWaitForFixedRotation; private boolean mWaitForFixedRotation; /** * The rotation that the display will apply after expanding PiP to fullscreen. This is only * meaningful if {@link #mWaitForFixedRotation} is true. */ private @Surface.Rotation int mNextRotation; /** /** * If set to {@code true}, no entering PiP transition would be kicked off and most likely * If set to {@code true}, no entering PiP transition would be kicked off and most likely * it's due to the fact that Launcher is handling the transition directly when swiping * it's due to the fact that Launcher is handling the transition directly when swiping Loading Loading @@ -313,35 +315,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; return; } } final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); if (initialConfig == null) { Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken); return; } mPipUiEventLoggerLogger.log( mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsState.getDisplayLayout().rotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); final Rect destinationBounds = mPipBoundsState.getDisplayBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; : TRANSITION_DIRECTION_LEAVE_PIP; if (orientationDiffers) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mState = State.EXITING_PIP; mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); // Send started callback though animation is ignored. sendOnPipTransitionStarted(direction); // Don't bother doing an animation if the display rotation differs or if it's in // a non-supported windowing mode applyWindowingModeChangeOnExit(wct, direction); mTaskOrganizer.applyTransaction(wct); // Send finished callback though animation is ignored. sendOnPipTransitionFinished(direction); } else { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); // We set to fullscreen here for now, but later it will be set to UNDEFINED for // We set to fullscreen here for now, but later it will be set to UNDEFINED for // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. Loading @@ -368,7 +350,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } }); }); } } } private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { // Reset the final windowing mode. // Reset the final windowing mode. Loading Loading @@ -399,7 +380,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .setDuration(mEnterExitAnimationDuration) .start(); .start(); mInitialState.remove(mToken.asBinder()); mState = State.EXITING_PIP; mState = State.EXITING_PIP; } } Loading @@ -424,7 +404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mToken = mTaskInfo.token; mToken = mTaskInfo.token; mState = State.TASK_APPEARED; mState = State.TASK_APPEARED; mLeash = leash; mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; mPictureInPictureParams = mTaskInfo.pictureInPictureParams; setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, mTaskInfo.topActivityInfo); mTaskInfo.topActivityInfo); Loading Loading @@ -606,6 +585,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override @Override public void onFixedRotationStarted(int displayId, int newRotation) { public void onFixedRotationStarted(int displayId, int newRotation) { mNextRotation = newRotation; mWaitForFixedRotation = true; mWaitForFixedRotation = true; } } Loading Loading @@ -645,7 +625,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { if (mState.isInPip() && fromRotation) { if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) { // Update bounds state to final destination first. It's important to do this // Update bounds state to final destination first. It's important to do this // before finishing & cancelling the transition animation so that the MotionHelper // before finishing & cancelling the transition animation so that the MotionHelper // bounds are synchronized to the destination bounds when the animation ends. // bounds are synchronized to the destination bounds when the animation ends. Loading Loading @@ -1052,11 +1032,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.w(TAG, "Abort animation, invalid leash"); Log.w(TAG, "Abort animation, invalid leash"); return; return; } } final int rotationDelta = mWaitForFixedRotation ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4 : Surface.ROTATION_0; Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE ? mPipBoundsState.getBounds() : currentBounds; ? mPipBoundsState.getBounds() : currentBounds; mPipAnimationController mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect, direction, startingAngle) sourceHintRect, direction, startingAngle, rotationDelta) .setTransitionDirection(direction) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .setDuration(durationMs) Loading Loading @@ -1101,11 +1084,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mInitialState:"); for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) { pw.println(innerPrefix + " binder=" + e.getKey() + " winConfig=" + e.getValue().windowConfiguration); } } } @Override @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.app.TaskInfo; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.IBinder; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.TransitionRequestInfo; Loading Loading @@ -112,7 +113,7 @@ public class PipTransition extends PipTransitionController { taskInfo.pictureInPictureParams, currentBounds); taskInfo.pictureInPictureParams, currentBounds); animator = mPipAnimationController.getAnimator(taskInfo, leash, animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */); TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { t.setAlpha(leash, 0f); t.setAlpha(leash, 0f); t.apply(); t.apply(); Loading Loading
core/java/android/app/ActivityThread.java +7 −0 Original line number Original line Diff line number Diff line Loading @@ -5685,6 +5685,13 @@ public final class ActivityThread extends ClientTransactionHandler { final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull( final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull( amOverrideConfig, contextThemeWrapperOverrideConfig); amOverrideConfig, contextThemeWrapperOverrideConfig); mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); final Resources res = activity.getResources(); if (res.hasOverrideDisplayAdjustments()) { // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated // configuration of activity may be sent later than the adjustments. In this case, the // adjustments need to be updated for the consistency of display info. res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig); } activity.mConfigChangeFlags = 0; activity.mConfigChangeFlags = 0; activity.mCurrentConfig = new Configuration(newConfig); activity.mCurrentConfig = new Configuration(newConfig); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +51 −4 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.wm.shell.pip; package com.android.wm.shell.pip; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.animation.AnimationHandler; import android.animation.AnimationHandler; import android.animation.Animator; import android.animation.Animator; import android.animation.RectEvaluator; import android.animation.RectEvaluator; Loading @@ -24,11 +27,13 @@ import android.annotation.IntDef; import android.app.TaskInfo; import android.app.TaskInfo; import android.graphics.Rect; import android.graphics.Rect; import android.view.Choreographer; import android.view.Choreographer; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.DisplayLayout; import java.lang.annotation.Retention; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -132,15 +137,21 @@ public class PipAnimationController { * leash bounds before transformation/any animation. This is so when we try to construct * leash bounds before transformation/any animation. This is so when we try to construct * the different transformation matrices for the animation, we are constructing this based off * the different transformation matrices for the animation, we are constructing this based off * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed. * * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the * rotation change. */ */ @VisibleForTesting @VisibleForTesting public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { if (mCurrentAnimator == null) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, endBounds, sourceHintRect, direction, 0 /* startingAngle */)); endBounds, sourceHintRect, direction, 0 /* startingAngle */, rotationDelta)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure // If we are still animating the fade into pip, then just move the surface and ensure Loading @@ -156,7 +167,7 @@ public class PipAnimationController { mCurrentAnimator.cancel(); mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, endBounds, sourceHintRect, direction, startingAngle)); endBounds, sourceHintRect, direction, startingAngle, rotationDelta)); } } return mCurrentAnimator; return mCurrentAnimator; } } Loading Loading @@ -410,7 +421,8 @@ public class PipAnimationController { static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle) { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { // Just for simplicity we'll interpolate between the source rect hint insets and empty // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop // insets to calculate the window crop final Rect initialSourceValue; final Rect initialSourceValue; Loading @@ -431,6 +443,16 @@ public class PipAnimationController { } } final Rect sourceInsets = new Rect(0, 0, 0, 0); final Rect sourceInsets = new Rect(0, 0, 0, 0); final Rect rotatedEndRect; if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { // Rotate the end bounds according to the rotation delta because the display will // be rotated to the same orientation. rotatedEndRect = new Rect(endValue); DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta); } else { rotatedEndRect = null; } // construct new Rect instances in case they are recycled // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), Loading @@ -444,6 +466,12 @@ 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 (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when // leaving PiP to fullscreen. applyRotation(tx, leash, fraction, start, end, rotatedEndRect); return; } Rect bounds = mRectEvaluator.evaluate(fraction, start, end); Rect bounds = mRectEvaluator.evaluate(fraction, start, end); float angle = (1.0f - fraction) * startingAngle; float angle = (1.0f - fraction) * startingAngle; setCurrentValue(bounds); setCurrentValue(bounds); Loading @@ -469,6 +497,25 @@ public class PipAnimationController { tx.apply(); tx.apply(); } } private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash, float fraction, Rect start, Rect end, Rect rotatedEndRect) { final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect); setCurrentValue(bounds); final float degree, x, y; if (rotationDelta == ROTATION_90) { degree = 90 * fraction; x = fraction * (end.right - start.left) + start.left; y = fraction * (end.top - start.top) + start.top; } else { degree = -90 * fraction; x = fraction * (end.left - start.left) + start.left; y = fraction * (end.bottom - start.top) + start.top; } getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds, rotatedEndRect, degree, x, y); tx.apply(); } @Override @Override void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { getSurfaceTransactionHelper() getSurfaceTransactionHelper() Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +26 −0 Original line number Original line Diff line number Diff line Loading @@ -126,6 +126,32 @@ public class PipSurfaceTransactionHelper { return this; return this; } } /** * Operates the rotation according to the given degrees and scale (setMatrix) according to the * source bounds and rotated destination bounds. The crop will be the unscaled source bounds. * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees, float positionX, float positionY) { mTmpDestinationRect.set(sourceBounds); final int dw = destinationBounds.width(); final int dh = destinationBounds.height(); // Scale by the short side so there won't be empty area if the aspect ratio of source and // destination are different. final float scale = dw <= dh ? (float) sourceBounds.width() / dw : (float) sourceBounds.height() / dh; // Inverse scale for crop to fit in screen coordinates. mTmpDestinationRect.scale(1 / scale); mTmpTransform.setRotate(degrees); mTmpTransform.postScale(scale, scale); mTmpTransform.postTranslate(positionX, positionY); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height()); return this; } /** /** * Resets the scale (setMatrix) on a given transaction and leash if there's any * Resets the scale (setMatrix) on a given transaction and leash if there's any * * Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +41 −63 Original line number Original line Diff line number Diff line Loading @@ -48,13 +48,12 @@ import android.app.TaskInfo; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import android.util.Log; import android.util.Rational; import android.util.Rational; import android.view.Display; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerToken; Loading @@ -72,8 +71,6 @@ import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Objects; import java.util.Optional; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Consumer; Loading Loading @@ -134,7 +131,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipUiEventLogger mPipUiEventLoggerLogger; private final PipUiEventLogger mPipUiEventLoggerLogger; private final int mEnterExitAnimationDuration; private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<LegacySplitScreen> mSplitScreenOptional; private final Optional<LegacySplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; protected final ShellExecutor mMainExecutor; Loading Loading @@ -191,6 +187,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ */ private boolean mWaitForFixedRotation; private boolean mWaitForFixedRotation; /** * The rotation that the display will apply after expanding PiP to fullscreen. This is only * meaningful if {@link #mWaitForFixedRotation} is true. */ private @Surface.Rotation int mNextRotation; /** /** * If set to {@code true}, no entering PiP transition would be kicked off and most likely * If set to {@code true}, no entering PiP transition would be kicked off and most likely * it's due to the fact that Launcher is handling the transition directly when swiping * it's due to the fact that Launcher is handling the transition directly when swiping Loading Loading @@ -313,35 +315,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; return; } } final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); if (initialConfig == null) { Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken); return; } mPipUiEventLoggerLogger.log( mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsState.getDisplayLayout().rotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); final Rect destinationBounds = mPipBoundsState.getDisplayBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; : TRANSITION_DIRECTION_LEAVE_PIP; if (orientationDiffers) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mState = State.EXITING_PIP; mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); // Send started callback though animation is ignored. sendOnPipTransitionStarted(direction); // Don't bother doing an animation if the display rotation differs or if it's in // a non-supported windowing mode applyWindowingModeChangeOnExit(wct, direction); mTaskOrganizer.applyTransaction(wct); // Send finished callback though animation is ignored. sendOnPipTransitionFinished(direction); } else { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); // We set to fullscreen here for now, but later it will be set to UNDEFINED for // We set to fullscreen here for now, but later it will be set to UNDEFINED for // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. Loading @@ -368,7 +350,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } }); }); } } } private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) { // Reset the final windowing mode. // Reset the final windowing mode. Loading Loading @@ -399,7 +380,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .setDuration(mEnterExitAnimationDuration) .start(); .start(); mInitialState.remove(mToken.asBinder()); mState = State.EXITING_PIP; mState = State.EXITING_PIP; } } Loading @@ -424,7 +404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mToken = mTaskInfo.token; mToken = mTaskInfo.token; mState = State.TASK_APPEARED; mState = State.TASK_APPEARED; mLeash = leash; mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; mPictureInPictureParams = mTaskInfo.pictureInPictureParams; setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, mTaskInfo.topActivityInfo); mTaskInfo.topActivityInfo); Loading Loading @@ -606,6 +585,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override @Override public void onFixedRotationStarted(int displayId, int newRotation) { public void onFixedRotationStarted(int displayId, int newRotation) { mNextRotation = newRotation; mWaitForFixedRotation = true; mWaitForFixedRotation = true; } } Loading Loading @@ -645,7 +625,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { if (mState.isInPip() && fromRotation) { if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) { // Update bounds state to final destination first. It's important to do this // Update bounds state to final destination first. It's important to do this // before finishing & cancelling the transition animation so that the MotionHelper // before finishing & cancelling the transition animation so that the MotionHelper // bounds are synchronized to the destination bounds when the animation ends. // bounds are synchronized to the destination bounds when the animation ends. Loading Loading @@ -1052,11 +1032,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.w(TAG, "Abort animation, invalid leash"); Log.w(TAG, "Abort animation, invalid leash"); return; return; } } final int rotationDelta = mWaitForFixedRotation ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4 : Surface.ROTATION_0; Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE ? mPipBoundsState.getBounds() : currentBounds; ? mPipBoundsState.getBounds() : currentBounds; mPipAnimationController mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect, direction, startingAngle) sourceHintRect, direction, startingAngle, rotationDelta) .setTransitionDirection(direction) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .setDuration(durationMs) Loading Loading @@ -1101,11 +1084,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mInitialState:"); for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) { pw.println(innerPrefix + " binder=" + e.getKey() + " winConfig=" + e.getValue().windowConfiguration); } } } @Override @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +2 −1 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.app.TaskInfo; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.graphics.Rect; import android.os.IBinder; import android.os.IBinder; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.TransitionRequestInfo; Loading Loading @@ -112,7 +113,7 @@ public class PipTransition extends PipTransitionController { taskInfo.pictureInPictureParams, currentBounds); taskInfo.pictureInPictureParams, currentBounds); animator = mPipAnimationController.getAnimator(taskInfo, leash, animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */); TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { t.setAlpha(leash, 0f); t.setAlpha(leash, 0f); t.apply(); t.apply(); Loading