Add transition when swiping from fullscreen to enter PiP
There are some artifacts actually come from test app implementation
- when in landscape fullscreen, sourceRectHint would include the black
background on both sides
- when settled in PiP, app does the transition which causes the final
glitch
Would address these later in the test app itself
Video: http://rcll/aaaaaabFQoRHlzixHdtY/cOco1yoMXwFzSifFqQET9U
Video: http://rcll/aaaaaabFQoRHlzixHdtY/f3IWG09DRO9aUNnzqSdPLU
Bug: 171802909
Test: see video
Change-Id: I783e51e78277b1179a7e8de50050b7c9e1a6de17
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 201dfe7..f82bc2d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
@@ -1065,7 +1068,7 @@
runningTaskTarget.pictureInPictureParams) != null;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
- homeAnimFactory, runningTaskTarget);
+ homeAnimFactory, runningTaskTarget, start);
mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
mSwipePipToHomeAnimator.setInterpolator(interpolator);
mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
@@ -1135,23 +1138,34 @@
}
private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
- RemoteAnimationTargetCompat runningTaskTarget) {
+ RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+ final int windowRotation = orientationState.getDisplayRotation();
+ final int homeRotation = orientationState.getRecentsActivityRotation();
final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
.startSwipePipToHome(taskInfo.topActivity,
TaskInfoCompat.getTopActivityInfo(taskInfo),
runningTaskTarget.pictureInPictureParams,
- orientationState.getRecentsActivityRotation(),
+ homeRotation,
mDp.hotseatBarSizePx);
+ final Rect startBounds = new Rect();
+ updateProgressForStartRect(new Matrix(), startProgress).round(startBounds);
final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
runningTaskTarget.taskId,
taskInfo.topActivity,
runningTaskTarget.leash.getSurfaceControl(),
TaskInfoCompat.getPipSourceRectHint(runningTaskTarget.pictureInPictureParams),
TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
+ startBounds,
destinationBounds);
+ // We would assume home and app window always in the same rotation While homeRotation
+ // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
+ if (homeRotation == ROTATION_0
+ && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
+ swipePipToHomeAnimator.setFromRotation(mTaskViewSimulator, windowRotation);
+ }
swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index f696efe..c0087b0 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -175,6 +175,24 @@
}
/**
+ * Update with start progress for window animation to home.
+ * @param outMatrix {@link Matrix} to map a rect in Launcher space to window space.
+ * @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
+ * @return {@link RectF} represents the bounds as starting point in window space.
+ */
+ protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
+ mCurrentShift.updateValue(startProgress);
+ mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+ RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+ mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
+
+ final RectF startRect = new RectF(cropRectF);
+ mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+ return startRect;
+ }
+
+ /**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
@@ -183,16 +201,11 @@
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- mCurrentShift.updateValue(startProgress);
- mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+ Matrix homeToWindowPositionMap = new Matrix();
+ final RectF startRect = updateProgressForStartRect(
+ homeToWindowPositionMap, startProgress);
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
- // Matrix to map a rect in Launcher space to window space
- Matrix homeToWindowPositionMap = new Matrix();
- mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
-
- final RectF startRect = new RectF(cropRectF);
- mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
// Move the startRect to Launcher space as floatingIconView runs in Launcher
Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 16ca43d..8fbd645 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -22,7 +22,11 @@
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -42,9 +46,12 @@
*/
public class SwipePipToHomeAnimator extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener {
+ private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+
private final int mTaskId;
private final ComponentName mComponentName;
private final SurfaceControl mLeash;
+ private final Rect mAppBounds = new Rect();
private final Rect mStartBounds = new Rect();
private final Rect mDestinationBounds = new Rect();
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -55,29 +62,50 @@
private final Rect mSourceHintRectInsets = new Rect();
private final Rect mSourceInsets = new Rect();
+ /** for rotation via {@link #setFromRotation(TaskViewSimulator, int)} */
+ private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+ private final Rect mDestinationBoundsTransformed = new Rect();
+ private final Rect mDestinationBoundsAnimation = new Rect();
+
/**
* Flag to avoid the double-end problem since the leash would have been released
* after the first end call and any further operations upon it would lead to NPE.
*/
private boolean mHasAnimationEnded;
+ /**
+ * @param taskId Task id associated with this animator, see also {@link #getTaskId()}
+ * @param componentName Component associated with this animator,
+ * see also {@link #getComponentName()}
+ * @param leash {@link SurfaceControl} this animator operates on
+ * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams}
+ * @param appBounds Bounds of the application, sourceRectHint is based on this bounds
+ * @param startBounds Bounds of the application when this animator starts. This can be
+ * different from the appBounds if user has swiped a certain distance and
+ * Launcher has performed transform on the leash.
+ * @param destinationBounds Bounds of the destination this animator ends to
+ */
public SwipePipToHomeAnimator(int taskId,
@NonNull ComponentName componentName,
@NonNull SurfaceControl leash,
@NonNull Rect sourceRectHint,
+ @NonNull Rect appBounds,
@NonNull Rect startBounds,
@NonNull Rect destinationBounds) {
mTaskId = taskId;
mComponentName = componentName;
mLeash = leash;
+ mAppBounds.set(appBounds);
mStartBounds.set(startBounds);
mDestinationBounds.set(destinationBounds);
+ mDestinationBoundsTransformed.set(mDestinationBounds);
+ mDestinationBoundsAnimation.set(mDestinationBounds);
mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
- mSourceHintRectInsets.set(sourceRectHint.left - startBounds.left,
- sourceRectHint.top - startBounds.top,
- startBounds.right - sourceRectHint.right,
- startBounds.bottom - sourceRectHint.bottom);
+ mSourceHintRectInsets.set(sourceRectHint.left - appBounds.left,
+ sourceRectHint.top - appBounds.top,
+ appBounds.right - sourceRectHint.right,
+ appBounds.bottom - sourceRectHint.bottom);
addListener(new AnimationSuccessListener() {
@Override
@@ -106,16 +134,62 @@
addUpdateListener(this);
}
+ /** sets the from rotation if it's different from the target rotation. */
+ public void setFromRotation(TaskViewSimulator taskViewSimulator,
+ @RecentsOrientedState.SurfaceRotation int fromRotation) {
+ if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
+ Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
+ return;
+ }
+ mFromRotation = fromRotation;
+ final Matrix matrix = new Matrix();
+ taskViewSimulator.applyWindowToHomeRotation(matrix);
+
+ // map the destination bounds into window space. mDestinationBounds is always calculated
+ // in the final home space and the animation runs in original window space.
+ final RectF transformed = new RectF(mDestinationBounds);
+ matrix.mapRect(transformed, new RectF(mDestinationBounds));
+ transformed.round(mDestinationBoundsTransformed);
+
+ // set the animation destination bounds for RectEvaluator calculation.
+ // bounds and insets are calculated as if the transition is from mAppBounds to
+ // mDestinationBoundsAnimation, separated from rotate / scale / position.
+ mDestinationBoundsAnimation.set(mAppBounds.left, mAppBounds.top,
+ mAppBounds.left + mDestinationBounds.width(),
+ mAppBounds.top + mDestinationBounds.height());
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator animator) {
if (mHasAnimationEnded) return;
final float fraction = animator.getAnimatedFraction();
- final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, mDestinationBounds);
+ final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
+ mDestinationBoundsAnimation);
final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
mSourceHintRectInsets);
- final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mStartBounds, bounds, insets);
+ final SurfaceControl.Transaction tx =
+ PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+ if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+ final float degree, positionX, positionY;
+ if (mFromRotation == Surface.ROTATION_90) {
+ degree = -90 * fraction;
+ positionX = fraction * (mDestinationBoundsTransformed.left - mAppBounds.left)
+ + mAppBounds.left;
+ positionY = fraction * (mDestinationBoundsTransformed.bottom - mAppBounds.top)
+ + mAppBounds.top;
+ } else {
+ degree = 90 * fraction;
+ positionX = fraction * (mDestinationBoundsTransformed.right - mAppBounds.left)
+ + mAppBounds.left;
+ positionY = fraction * (mDestinationBoundsTransformed.top - mAppBounds.top)
+ + mAppBounds.top;
+ }
+ mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+ degree, positionX, positionY);
+ } else {
+ mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+ }
mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
tx.apply();
}
@@ -135,8 +209,9 @@
private void onAnimationEnd() {
if (mHasAnimationEnded) return;
- final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBounds);
+ final SurfaceControl.Transaction tx =
+ PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+ mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBoundsTransformed, mFromRotation);
tx.apply();
mHasAnimationEnded = true;
}