diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 7dc58a58c3..7bea5935c4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -415,6 +415,7 @@ public class WindowTransformSwipeHandler }); mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper); mRecentsView.setClipAnimationHelper(mClipAnimationHelper); + mRecentsView.setLiveTileOverlay(mLiveTileOverlay); mActivity.getRootView().getOverlay().add(mLiveTileOverlay); mStateCallback.setState(STATE_LAUNCHER_PRESENT); @@ -822,6 +823,7 @@ public class WindowTransformSwipeHandler setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); duration = Math.max(MIN_OVERSHOOT_DURATION, duration); } else if (endTarget == RECENTS) { + mLiveTileOverlay.startIconAnimation(); mRecentsAnimationWrapper.enableInputProxy(); if (mRecentsView != null) { duration = Math.max(duration, mRecentsView.getScroller().getDuration()); @@ -1172,7 +1174,7 @@ public class WindowTransformSwipeHandler mActivityControlHelper.onSwipeUpComplete(mActivity); // Animate the first icon. - mRecentsView.animateUpRunningTaskIconScale(); + mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation()); mRecentsView.setSwipeDownShouldLaunchApp(true); RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java index ab2b90ff10..a83879738d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java @@ -1,5 +1,11 @@ package com.android.quickstep.views; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; @@ -9,16 +15,37 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.util.FloatProperty; + +import com.android.launcher3.anim.Interpolators; public class LiveTileOverlay extends Drawable { + private static final long ICON_ANIM_DURATION = 120; + + private static final FloatProperty PROGRESS = + new FloatProperty("progress") { + @Override + public void setValue(LiveTileOverlay liveTileOverlay, float progress) { + liveTileOverlay.setIconAnimationProgress(progress); + } + + @Override + public Float get(LiveTileOverlay liveTileOverlay) { + return liveTileOverlay.mIconAnimationProgress; + } + }; + private final Paint mPaint = new Paint(); private Rect mBoundsRect = new Rect(); private RectF mCurrentRect; private float mCornerRadius; + private Drawable mIcon; + private Animator mIconAnimator; private boolean mDrawEnabled = true; + private float mIconAnimationProgress = 0f; public LiveTileOverlay() { mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); @@ -35,6 +62,33 @@ public class LiveTileOverlay extends Drawable { invalidateSelf(); } + public void setIcon(Drawable icon) { + mIcon = icon; + } + + public void startIconAnimation() { + if (mIconAnimator != null) { + mIconAnimator.cancel(); + } + // This animator must match the icon part of {@link TaskView#FOCUS_TRANSITION} animation. + mIconAnimator = ObjectAnimator.ofFloat(this, PROGRESS, 1); + mIconAnimator.setDuration(ICON_ANIM_DURATION).setInterpolator(LINEAR); + mIconAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIconAnimator = null; + } + }); + mIconAnimator.start(); + } + + public float cancelIconAnimation() { + if (mIconAnimator != null) { + mIconAnimator.cancel(); + } + return mIconAnimationProgress; + } + public void setDrawEnabled(boolean drawEnabled) { if (mDrawEnabled != drawEnabled) { mDrawEnabled = drawEnabled; @@ -46,6 +100,16 @@ public class LiveTileOverlay extends Drawable { public void draw(Canvas canvas) { if (mCurrentRect != null && mDrawEnabled) { canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint); + if (mIcon != null && mIconAnimationProgress > 0f) { + canvas.save(); + float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f, + 1f).getInterpolation(mIconAnimationProgress); + canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale, + mCurrentRect.top - mIcon.getBounds().height() / 2 * scale); + canvas.scale(scale, scale); + mIcon.draw(canvas); + canvas.restore(); + } } } @@ -59,4 +123,9 @@ public class LiveTileOverlay extends Drawable { public int getOpacity() { return PixelFormat.TRANSLUCENT; } + + private void setIconAnimationProgress(float progress) { + mIconAnimationProgress = progress; + invalidateSelf(); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 3e0e8ae583..2583ffb0d9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -278,6 +278,7 @@ public abstract class RecentsView extends PagedView impl private final int mEmptyMessagePadding; private boolean mShowEmptyMessage; private Layout mEmptyTextLayout; + private LiveTileOverlay mLiveTileOverlay; private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { @@ -855,10 +856,15 @@ public abstract class RecentsView extends PagedView impl } public void animateUpRunningTaskIconScale() { + animateUpRunningTaskIconScale(0); + } + + public void animateUpRunningTaskIconScale(float startProgress) { mRunningTaskIconScaledDown = false; TaskView firstTask = getRunningTaskView(); if (firstTask != null) { firstTask.animateIconScaleAndDimIntoView(); + firstTask.setIconScaleAnimStartProgress(startProgress); } } @@ -1567,6 +1573,14 @@ public abstract class RecentsView extends PagedView impl mClipAnimationHelper = clipAnimationHelper; } + public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) { + mLiveTileOverlay = liveTileOverlay; + } + + public void updateLiveTileIcon(Drawable icon) { + mLiveTileOverlay.setIcon(icon); + } + public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) { if (mRecentsAnimationWrapper == null) { if (onFinishComplete != null) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 419a66674d..9eec584a27 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -156,7 +156,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mZoomScale; private float mFullscreenProgress; - private Animator mIconAndDimAnimator; + private ObjectAnimator mIconAndDimAnimator; + private float mIconScaleAnimStartProgress = 0; private float mFocusTransitionProgress = 1; private boolean mShowScreenshot; @@ -317,6 +318,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mIconLoadRequest = iconCache.updateIconInBackground(mTask, (task) -> { setIcon(task.icon); + if (isRunningTask()) { + getRecentsView().updateLiveTileIcon(task.icon); + } mDigitalWellBeingToast.initialize( mTask, (saturation, contentDescription) -> { @@ -380,11 +384,16 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mIconView.setScaleY(scale); } + public void setIconScaleAnimStartProgress(float startProgress) { + mIconScaleAnimStartProgress = startProgress; + } + public void animateIconScaleAndDimIntoView() { if (mIconAndDimAnimator != null) { mIconAndDimAnimator.cancel(); } mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1); + mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress); mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR); mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() { @Override