summaryrefslogtreecommitdiffstats
path: root/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java')
-rw-r--r--quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java207
1 files changed, 183 insertions, 24 deletions
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 493e9e238..8c7f104a6 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -15,21 +15,44 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Build;
+import android.os.RemoteException;
+import android.support.annotation.Nullable;
+import android.view.animation.Interpolator;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.function.BiConsumer;
/**
* Utility class to handle window clip animation
*/
+@TargetApi(Build.VERSION_CODES.P)
public class ClipAnimationHelper {
// The bounds of the source app in device coordinates
@@ -40,8 +63,8 @@ public class ClipAnimationHelper {
private final RectF mSourceRect = new RectF();
// The bounds of the task view in launcher window coordinates
private final RectF mTargetRect = new RectF();
- // Doesn't change after initialized, used as an anchor when changing mTargetRect
- private final RectF mInitialTargetRect = new RectF();
+ // Set when the final window destination is changed, such as offsetting for quick scrub
+ private final PointF mTargetOffset = new PointF();
// The insets to be used for clipping the app window, which can be larger than mSourceInsets
// if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
// app window coordinates.
@@ -54,15 +77,33 @@ public class ClipAnimationHelper {
private final Rect mClipRect = new Rect();
private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
private final Matrix mTmpMatrix = new Matrix();
+ private final RectF mTmpRectF = new RectF();
+ private float mTargetScale = 1f;
+ private Interpolator mInterpolator = LINEAR;
+ // We translate y slightly faster than the rest of the animation for quick scrub.
+ private Interpolator mOffsetYInterpolator = LINEAR;
- public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
- mHomeStackBounds.set(homeStackBounds);
- mSourceInsets.set(target.getContentInsets());
+ // Whether to boost the opening animation target layers, or the closing
+ private int mBoostModeTargetLayers = -1;
+ // Wether or not applyTransform has been called yet since prepareAnimation()
+ private boolean mIsFirstFrame = true;
+
+ private BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> mTaskTransformCallback =
+ (t, a) -> { };
+
+ private void updateSourceStack(RemoteAnimationTargetCompat target) {
+ mSourceInsets.set(target.contentInsets);
mSourceStackBounds.set(target.sourceContainerBounds);
// TODO: Should sourceContainerBounds already have this offset?
mSourceStackBounds.offsetTo(target.position.x, target.position.y);
+
+ }
+
+ public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
+ mHomeStackBounds.set(homeStackBounds);
+ updateSourceStack(target);
}
public void updateTargetRect(Rect targetRect) {
@@ -73,8 +114,6 @@ public class ClipAnimationHelper {
mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
mHomeStackBounds.top - mSourceStackBounds.top);
- mInitialTargetRect.set(mTargetRect);
-
// Calculate the clip based on the target rect (since the content insets and the
// launcher insets may differ, so the aspect ratio of the target rect can differ
// from the source rect. The difference between the target rect (scaled to the
@@ -91,12 +130,22 @@ public class ClipAnimationHelper {
mSourceRect.set(scaledTargetRect);
}
- public void applyTransform(RemoteAnimationTargetCompat[] targets, float progress) {
+ public void prepareAnimation(boolean isOpening) {
+ mIsFirstFrame = true;
+ mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+ }
+
+ public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
RectF currentRect;
- synchronized (mTargetRect) {
- currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+ mTmpRectF.set(mTargetRect);
+ Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
+ float offsetYProgress = mOffsetYInterpolator.getInterpolation(progress);
+ progress = mInterpolator.getInterpolation(progress);
+ currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+
+ synchronized (mTargetOffset) {
// Stay lined up with the center of the target, since it moves for quick scrub.
- currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0);
+ currentRect.offset(mTargetOffset.x * progress, mTargetOffset.y * offsetYProgress);
}
mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
@@ -106,30 +155,140 @@ public class ClipAnimationHelper {
mClipRect.bottom = (int)
(mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
- mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
-
TransactionCompat transaction = new TransactionCompat();
- for (RemoteAnimationTargetCompat app : targets) {
- if (app.mode == MODE_CLOSING) {
+ if (mIsFirstFrame) {
+ RemoteAnimationProvider.prepareTargetsForFirstFrame(targetSet.unfilteredApps,
+ transaction, mBoostModeTargetLayers);
+ mIsFirstFrame = false;
+ }
+ for (RemoteAnimationTargetCompat app : targetSet.apps) {
+ if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
mTmpMatrix.postTranslate(app.position.x, app.position.y);
transaction.setMatrix(app.leash, mTmpMatrix)
.setWindowCrop(app.leash, mClipRect);
- if (app.isNotInRecents) {
- transaction.setAlpha(app.leash, 1 - progress);
- }
+ }
- transaction.show(app.leash);
+ if (app.isNotInRecents
+ || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ transaction.setAlpha(app.leash, 1 - progress);
}
+
+ mTaskTransformCallback.accept(transaction, app);
}
transaction.setEarlyWakeup();
transaction.apply();
+ return currentRect;
}
- public void offsetTarget(float scale, float offsetX, float offsetY) {
- synchronized (mTargetRect) {
- mTargetRect.set(mInitialTargetRect);
- Utilities.scaleRectFAboutCenter(mTargetRect, scale);
- mTargetRect.offset(offsetX, offsetY);
+ public void setTaskTransformCallback
+ (BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> callback) {
+ mTaskTransformCallback = callback;
+ }
+
+ public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
+ synchronized (mTargetOffset) {
+ mTargetOffset.set(offsetX, offsetY);
}
+ mTargetScale = scale;
+ mInterpolator = interpolator;
+ mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
+ QUICK_SCRUB_TRANSLATION_Y_FACTOR);
+ }
+
+ public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
+ fromTaskThumbnailView(ttv, rv, null);
+ }
+
+ public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
+ @Nullable RemoteAnimationTargetCompat target) {
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
+ BaseDragLayer dl = activity.getDragLayer();
+
+ int[] pos = new int[2];
+ dl.getLocationOnScreen(pos);
+ mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
+ mHomeStackBounds.offset(pos[0], pos[1]);
+
+ if (target != null) {
+ updateSourceStack(target);
+ } else if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
+ updateStackBoundsToMultiWindowTaskSize(activity);
+ } else {
+ mSourceStackBounds.set(mHomeStackBounds);
+ mSourceInsets.set(activity.getDeviceProfile().getInsets());
+ }
+
+ Rect targetRect = new Rect();
+ dl.getDescendantRectRelativeToSelf(ttv, targetRect);
+ updateTargetRect(targetRect);
+
+ // Transform the clip relative to the target rect.
+ float scale = mTargetRect.width() / mSourceRect.width();
+ mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
+ mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
+ mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
+ mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
+ }
+
+ private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
+ if (sysUiProxy != null) {
+ try {
+ mSourceStackBounds.set(sysUiProxy.getNonMinimizedSplitScreenSecondaryBounds());
+ return;
+ } catch (RemoteException e) {
+ // Use half screen size
+ }
+ }
+
+ // Assume that the task size is half screen size (minus the insets and the divider size)
+ DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
+ // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+ // account for system insets
+ int taskWidth = fullDp.availableWidthPx;
+ int taskHeight = fullDp.availableHeightPx;
+ int halfDividerSize = activity.getResources()
+ .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+ Rect insets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(insets);
+ if (fullDp.isLandscape) {
+ taskWidth = taskWidth / 2 - halfDividerSize;
+ } else {
+ taskHeight = taskHeight / 2 - halfDividerSize;
+ }
+
+ // Align the task to bottom left/right edge (closer to nav bar).
+ int left = activity.getDeviceProfile().isSeascape() ? insets.left
+ : (insets.left + fullDp.availableWidthPx - taskWidth);
+ mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
+ mSourceStackBounds.offset(left, insets.top + fullDp.availableHeightPx - taskHeight);
+ }
+
+ public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
+ RectF currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+ canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
+ mSourceStackBounds.top - mHomeStackBounds.top);
+ mTmpMatrix.setRectToRect(mTargetRect, currentRect, ScaleToFit.FILL);
+
+ canvas.concat(mTmpMatrix);
+ canvas.translate(mTargetRect.left, mTargetRect.top);
+
+ float insetProgress = (1 - progress);
+ ttv.drawOnCanvas(canvas,
+ -mSourceWindowClipInsets.left * insetProgress,
+ -mSourceWindowClipInsets.top * insetProgress,
+ ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
+ ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
+ ttv.getCornerRadius() * progress);
+ }
+
+ public RectF getTargetRect() {
+ return mTargetRect;
+ }
+
+ public RectF getSourceRect() {
+ return mSourceRect;
}
}