From 7d783ff70f352d3edf9a1e70744530d7de4b3017 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 21 Aug 2024 09:06:43 -0700 Subject: [PATCH] Add animation for the predicted icon ring Animation is modeled after the notification dot animation. - This avoids the ring jumping in after swiping up to home to a predicted app. - Also decreased the amount of time the icon has to fade out so that it shows up less during the animation. Fixes: 360115794 Test: open predicted app from hotseat, swipe up to return home Flag: EXEMPT bugfix Change-Id: I59659610eb2e9f375a408d553bb4aa80947a28c4 --- .../uioverrides/PredictedAppIcon.java | 66 ++++++++++++++++++- src/com/android/launcher3/BubbleTextView.java | 2 +- .../launcher3/views/FloatingIconView.java | 5 +- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 110ca167aa..37e00341e6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -20,6 +20,7 @@ import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.Keyframe; @@ -39,6 +40,7 @@ import android.graphics.drawable.Drawable; import android.os.Process; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Property; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -71,12 +73,27 @@ import java.util.List; */ public class PredictedAppIcon extends DoubleShadowBubbleTextView { + private static final float RING_SCALE_START_VALUE = 0.75f; private static final int RING_SHADOW_COLOR = 0x99000000; private static final float RING_EFFECT_RATIO = 0.095f; private static final long ICON_CHANGE_ANIM_DURATION = 360; private static final long ICON_CHANGE_ANIM_STAGGER = 50; + private static final Property RING_SCALE_PROPERTY = + new Property<>(Float.TYPE, "ringScale") { + @Override + public Float get(PredictedAppIcon icon) { + return icon.mRingScale; + } + + @Override + public void set(PredictedAppIcon icon, Float value) { + icon.mRingScale = value; + icon.invalidate(); + } + }; + boolean mIsDrawingDot = false; private final DeviceProfile mDeviceProfile; private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -96,6 +113,11 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { private Animator mSlotMachineAnim; private float mSlotMachineIconTranslationY; + // Used to animate the "ring" around predicted icons + private float mRingScale = 1f; + private boolean mForceHideRing = false; + private Animator mRingScaleAnim; + private static final FloatProperty SLOT_MACHINE_TRANSLATION_Y = new FloatProperty("slotMachineTranslationY") { @Override @@ -356,17 +378,57 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { } } + @Override + public void setIconVisible(boolean visible) { + setForceHideRing(!visible); + super.setIconVisible(visible); + } + + private void setForceHideRing(boolean forceHideRing) { + if (mForceHideRing == forceHideRing) { + return; + } + mForceHideRing = forceHideRing; + + if (forceHideRing) { + invalidate(); + } else { + animateRingScale(RING_SCALE_START_VALUE, 1); + } + } + + private void cancelRingScaleAnim() { + if (mRingScaleAnim != null) { + mRingScaleAnim.cancel(); + } + } + + private void animateRingScale(float... ringScale) { + cancelRingScaleAnim(); + mRingScaleAnim = ObjectAnimator.ofFloat(this, RING_SCALE_PROPERTY, ringScale); + mRingScaleAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRingScaleAnim = null; + } + }); + mRingScaleAnim.start(); + } + private void drawEffect(Canvas canvas) { - // Don't draw ring effect if item is about to be dragged. - if (mDrawForDrag) { + // Don't draw ring effect if item is about to be dragged or if the icon is not visible. + if (mDrawForDrag || !mIsIconVisible) { return; } mIconRingPaint.setColor(RING_SHADOW_COLOR); mIconRingPaint.setMaskFilter(mShadowFilter); + int count = canvas.save(); + canvas.scale(mRingScale, mRingScale, canvas.getWidth() / 2f, canvas.getHeight() / 2f); canvas.drawPath(mRingPath, mIconRingPaint); mIconRingPaint.setColor(mPlateColor); mIconRingPaint.setMaskFilter(null); canvas.drawPath(mRingPath, mIconRingPaint); + canvas.restoreToCount(count); } @Override diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 1eccbff00a..0839b67350 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -176,7 +176,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @ViewDebug.ExportedProperty(category = "launcher") private boolean mSkipUserBadge = false; @ViewDebug.ExportedProperty(category = "launcher") - private boolean mIsIconVisible = true; + protected boolean mIsIconVisible = true; @ViewDebug.ExportedProperty(category = "launcher") private int mTextColor; @ViewDebug.ExportedProperty(category = "launcher") diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 1e577bec36..37482ac44d 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -175,8 +175,9 @@ public class FloatingIconView extends FrameLayout implements mLauncher.getDeviceProfile(), taskViewDrawAlpha); if (mFadeOutView != null) { - // The alpha goes from 1 to 0 when progress is 0 and 0.33 respectively. - mFadeOutView.setAlpha(1 - Math.min(1f, mapToRange(progress, 0, 0.33f, 0, 1, LINEAR))); + // The alpha goes from 1 to 0 when progress is 0 and 0.15 respectively. + // This value minimizes view display time while still allowing the view to fade out. + mFadeOutView.setAlpha(1 - Math.min(1f, mapToRange(progress, 0, 0.15f, 0, 1, LINEAR))); } }