Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d5a8dc8 authored by Android Build Prod User's avatar Android Build Prod User Committed by Android (Google) Code Review
Browse files

Merge changes Ib2bc8419,Ia41cb0e3,I43bfbc41 into sc-v2-dev

* changes:
  Animate PredictedAppIcon when its icon changes
  Add "wave" animation when entering taskbar edu
  Add slot machine animation for PredictedAppIcon
parents b862d2c3 175d152f
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -166,5 +166,6 @@
    <dimen name="taskbar_stashed_size">24dp</dimen>
    <dimen name="taskbar_stashed_handle_width">220dp</dimen>
    <dimen name="taskbar_stashed_handle_height">6dp</dimen>
    <dimen name="taskbar_edu_bg_corner_radius">28dp</dimen>
    <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
    <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
</resources>
+6 −1
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ public class HotseatPredictionController implements DragController.DragListener,
        }

        int predictionIndex = 0;
        int numViewsAnimated = 0;
        ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
        // make sure predicted icon removal and filling predictions don't step on each other
        if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
@@ -233,7 +234,11 @@ public class HotseatPredictionController implements DragController.DragListener,
                    (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
            if (isPredictedIcon(child) && child.isEnabled()) {
                PredictedAppIcon icon = (PredictedAppIcon) child;
                icon.applyFromWorkspaceItem(predictedItem);
                boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
                icon.applyFromWorkspaceItem(predictedItem, animateIconChange, numViewsAnimated);
                if (animateIconChange) {
                    numViewsAnimated++;
                }
                icon.finishBinding(mPredictionLongClickListener);
            } else {
                newItems.add(predictedItem);
+9 −0
Original line number Diff line number Diff line
@@ -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<ItemInfoWithIcon> getAppIconsForEdu() {
        return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
    }

    /**
     * Starts the taskbar education flow, if the user hasn't seen it yet.
     */
+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public class TaskbarControllers {
        taskbarKeyguardController.init(navbarButtonsViewController);
        stashedHandleViewController.init(this);
        taskbarStashController.init(this);
        taskbarEduController.init(this);
    }

    /**
+141 −0
Original line number Diff line number Diff line
@@ -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<BitmapInfo> 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.
Loading