From 31f787d3527734f89484fcfc95e95a33d8a8d846 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 24 Aug 2021 10:56:31 -0700 Subject: [PATCH] Add "wave" animation when entering taskbar edu Each icon scales and translates up, then back down. If the icon is predicted, it also plays a "slot machine" animation through random icons from AllAppsList. Test: Visual Bug: 180605356 Change-Id: Ia41cb0e340347eea6b580d23c8a2386837e9c399 --- quickstep/res/values/dimens.xml | 3 +- .../taskbar/LauncherTaskbarUIController.java | 9 ++ .../launcher3/taskbar/TaskbarControllers.java | 1 + .../taskbar/TaskbarEduController.java | 141 ++++++++++++++++++ .../launcher3/taskbar/TaskbarEduView.java | 7 +- .../taskbar/TaskbarUIController.java | 8 + .../launcher3/taskbar/TaskbarView.java | 12 ++ .../taskbar/TaskbarViewController.java | 4 + 8 files changed, 181 insertions(+), 4 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index f1f23c46fc..1ec5bb8ed2 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -166,5 +166,6 @@ 24dp 220dp 6dp - 28dp + 25dp + 4dp diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index da10bfb0fa..acabb0d9f7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -34,6 +34,7 @@ import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.OnboardingPrefs; @@ -45,6 +46,9 @@ import com.android.quickstep.SystemUiProxy; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.ThumbnailData; +import java.util.Arrays; +import java.util.stream.Stream; + /** * A data source which integrates with a Launcher instance */ @@ -268,6 +272,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1); } + @Override + public Stream getAppIconsForEdu() { + return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps()); + } + /** * Starts the taskbar education flow, if the user hasn't seen it yet. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index b32a41e987..11975430b1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -78,6 +78,7 @@ public class TaskbarControllers { taskbarKeyguardController.init(navbarButtonsViewController); stashedHandleViewController.init(this); taskbarStashController.init(this); + taskbarEduController.init(this); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java index ae9592d779..fd5c2ea6b2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java @@ -15,16 +15,72 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.anim.Interpolators.ACCEL_2; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.Keyframe; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.TimeInterpolator; +import android.content.res.Resources; +import android.text.TextUtils; +import android.view.View; + import com.android.launcher3.R; +import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.uioverrides.PredictedAppIcon; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; /** Handles the Taskbar Education flow. */ public class TaskbarEduController { + private static final long WAVE_ANIM_DELAY = 250; + private static final long WAVE_ANIM_STAGGER = 50; + private static final long WAVE_ANIM_EACH_ICON_DURATION = 633; + private static final long WAVE_ANIM_SLOT_MACHINE_DURATION = 1085; + // The fraction of each icon's animation at which we reach the top point of the wave. + private static final float WAVE_ANIM_FRACTION_TOP = 0.4f; + // The fraction of each icon's animation at which we reach the bottom, before overshooting. + private static final float WAVE_ANIM_FRACTION_BOTTOM = 0.9f; + private static final TimeInterpolator WAVE_ANIM_TO_TOP_INTERPOLATOR = FAST_OUT_SLOW_IN; + private static final TimeInterpolator WAVE_ANIM_TO_BOTTOM_INTERPOLATOR = ACCEL_2; + private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_INTERPOLATOR = DEACCEL; + private static final TimeInterpolator WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR = ACCEL_DEACCEL; + private static final float WAVE_ANIM_ICON_SCALE = 1.2f; + // How many icons to cycle through in the slot machine (+ the original icon at each end). + private static final int WAVE_ANIM_SLOT_MACHINE_NUM_ICONS = 3; + private final TaskbarActivityContext mActivity; + private final float mWaveAnimTranslationY; + private final float mWaveAnimTranslationYReturnOvershoot; + + // Initialized in init. + TaskbarControllers mControllers; + private TaskbarEduView mTaskbarEduView; + private Animator mAnim; public TaskbarEduController(TaskbarActivityContext activity) { mActivity = activity; + + final Resources resources = activity.getResources(); + mWaveAnimTranslationY = resources.getDimension(R.dimen.taskbar_edu_wave_anim_trans_y); + mWaveAnimTranslationYReturnOvershoot = resources.getDimension( + R.dimen.taskbar_edu_wave_anim_trans_y_return_overshoot); + } + + public void init(TaskbarControllers controllers) { + mControllers = controllers; } void showEdu() { @@ -35,6 +91,7 @@ public class TaskbarEduController { mTaskbarEduView.init(new TaskbarEduCallbacks()); mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null); mTaskbarEduView.show(); + startAnim(createWaveAnim()); }); } @@ -44,6 +101,90 @@ public class TaskbarEduController { } } + /** + * Starts the given animation, ending the previous animation first if it's still playing. + */ + private void startAnim(Animator anim) { + if (mAnim != null) { + mAnim.end(); + } + mAnim = anim; + mAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnim = null; + } + }); + mAnim.start(); + } + + /** + * Creates a staggered "wave" animation where each icon translates and scales up in succession. + */ + private Animator createWaveAnim() { + AnimatorSet waveAnim = new AnimatorSet(); + View[] icons = mControllers.taskbarViewController.getIconViews(); + for (int i = 0; i < icons.length; i++) { + View icon = icons[i]; + AnimatorSet iconAnim = new AnimatorSet(); + + Keyframe[] scaleKeyframes = new Keyframe[] { + Keyframe.ofFloat(0, 1f), + Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, WAVE_ANIM_ICON_SCALE), + Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 1f), + Keyframe.ofFloat(1f, 1f) + }; + scaleKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR); + scaleKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR); + + Keyframe[] translationYKeyframes = new Keyframe[] { + Keyframe.ofFloat(0, 0f), + Keyframe.ofFloat(WAVE_ANIM_FRACTION_TOP, -mWaveAnimTranslationY), + Keyframe.ofFloat(WAVE_ANIM_FRACTION_BOTTOM, 0f), + // Half of the remaining fraction overshoots, then the other half returns to 0. + Keyframe.ofFloat( + WAVE_ANIM_FRACTION_BOTTOM + (1 - WAVE_ANIM_FRACTION_BOTTOM) / 2f, + mWaveAnimTranslationYReturnOvershoot), + Keyframe.ofFloat(1f, 0f) + }; + translationYKeyframes[1].setInterpolator(WAVE_ANIM_TO_TOP_INTERPOLATOR); + translationYKeyframes[2].setInterpolator(WAVE_ANIM_TO_BOTTOM_INTERPOLATOR); + translationYKeyframes[3].setInterpolator(WAVE_ANIM_OVERSHOOT_INTERPOLATOR); + translationYKeyframes[4].setInterpolator(WAVE_ANIM_OVERSHOOT_RETURN_INTERPOLATOR); + + iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon, + PropertyValuesHolder.ofKeyframe(SCALE_PROPERTY, scaleKeyframes)) + .setDuration(WAVE_ANIM_EACH_ICON_DURATION)); + iconAnim.play(ObjectAnimator.ofPropertyValuesHolder(icon, + PropertyValuesHolder.ofKeyframe(View.TRANSLATION_Y, translationYKeyframes)) + .setDuration(WAVE_ANIM_EACH_ICON_DURATION)); + + if (icon instanceof PredictedAppIcon) { + // Play slot machine animation through random icons from AllAppsList. + PredictedAppIcon predictedAppIcon = (PredictedAppIcon) icon; + ItemInfo itemInfo = (ItemInfo) icon.getTag(); + List iconsToAnimate = mControllers.uiController.getAppIconsForEdu() + .filter(appInfo -> !TextUtils.equals(appInfo.title, itemInfo.title)) + .map(appInfo -> appInfo.bitmap) + .filter(bitmap -> !bitmap.isNullOrLowRes()) + .collect(Collectors.toList()); + // Pick n icons at random. + Collections.shuffle(iconsToAnimate); + if (iconsToAnimate.size() > WAVE_ANIM_SLOT_MACHINE_NUM_ICONS) { + iconsToAnimate = iconsToAnimate.subList(0, WAVE_ANIM_SLOT_MACHINE_NUM_ICONS); + } + Animator slotMachineAnim = predictedAppIcon.createSlotMachineAnim(iconsToAnimate); + if (slotMachineAnim != null) { + iconAnim.play(slotMachineAnim.setDuration(WAVE_ANIM_SLOT_MACHINE_DURATION)); + } + } + + iconAnim.setStartDelay(WAVE_ANIM_STAGGER * i); + waveAnim.play(iconAnim); + } + waveAnim.setStartDelay(WAVE_ANIM_DELAY); + return waveAnim; + } /** * Callbacks for {@link TaskbarEduView} to interact with its controller. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java index 9c4e844fa8..8525427a64 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.taskbar; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import android.animation.PropertyValuesHolder; import android.content.Context; @@ -33,6 +33,7 @@ import com.android.launcher3.views.AbstractSlideInView; public class TaskbarEduView extends AbstractSlideInView implements Insettable { + private static final int DEFAULT_OPEN_DURATION = 500; private static final int DEFAULT_CLOSE_DURATION = 200; private final Rect mInsets = new Rect(); @@ -129,8 +130,8 @@ public class TaskbarEduView extends AbstractSlideInView mIsOpen = true; mOpenCloseAnimator.setValues( PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE); + mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start(); } void snapToPage(int page) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index df88e02d5e..c0312a01ac 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -17,6 +17,10 @@ package com.android.launcher3.taskbar; import android.graphics.Rect; +import com.android.launcher3.model.data.ItemInfoWithIcon; + +import java.util.stream.Stream; + /** * Base class for providing different taskbar UI */ @@ -35,4 +39,8 @@ public class TaskbarUIController { protected void updateContentInsets(Rect outContentInsets) { } protected void onStashedInAppChanged() { } + + public Stream getAppIconsForEdu() { + return Stream.empty(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index a4a92f7567..f9767f3c2d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -256,6 +256,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar return mIconLayoutBounds; } + /** + * Returns the app icons currently shown in the taskbar. + */ + public View[] getIconViews() { + final int count = getChildCount(); + View[] icons = new View[count]; + for (int i = 0; i < count; i++) { + icons[i] = getChildAt(i); + } + return icons; + } + // FolderIconParent implemented methods. @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 1882762a67..1f763860cf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -111,6 +111,10 @@ public class TaskbarViewController { return mTaskbarView.getIconLayoutBounds(); } + public View[] getIconViews() { + return mTaskbarView.getIconViews(); + } + public AnimatedFloat getTaskbarIconScaleForStash() { return mTaskbarIconScaleForStash; }