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

Commit c1d26448 authored by Alex Chau's avatar Alex Chau
Browse files

Animate inline QSB between home and apps

- Added a QSB to taskbar to be animated between inline QSB and taskbar
- Passed endValue of taskbar animator to TaskbarController to have aniamtion variation between home -> app and app -> home
- Tuned duartion of taskbar animation and also stagger animation when taskbar is present
- Disabled scaling down of taskbar and inline QSB for home -> app
- Disabled stagger animation of taskbar and inline QSB for app -> home

Bug: 220733187
Test: manual
Change-Id: I4aac0bbc343b992a0472298595770e2bf2a55990
parent b751033f
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
@@ -184,6 +183,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
    public static final int SPLIT_DIVIDER_ANIM_DURATION = 100;

    public static final int CONTENT_ALPHA_DURATION = 217;
    public static final int TASKBAR_TO_APP_DURATION = 600;
    // TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
    // is solved.
    public static final int TASKBAR_TO_HOME_DURATION = 300;
    protected static final int CONTENT_SCALE_DURATION = 350;
    protected static final int CONTENT_SCRIM_DURATION = 350;

@@ -527,7 +530,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
            workspace.forEachVisiblePage(
                    view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));

            // Do not scale hotseat as a whole when taskbar is present, and scale QSB only if it's
            // not inline.
            if (mDeviceProfile.isTaskbarPresent) {
                if (!mDeviceProfile.isQsbInline) {
                    viewsToAnimate.add(mLauncher.getHotseat().getQsb());
                }
            } else {
                viewsToAnimate.add(mLauncher.getHotseat());
            }

            viewsToAnimate.forEach(view -> {
                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+3 −1
Original line number Diff line number Diff line
@@ -171,7 +171,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
                isResumed,
                fromInit,
                /* startAnimation= */ true,
                QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
                !isResumed
                        ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
                        : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
    }

    @Nullable
+45 −13
Original line number Diff line number Diff line
@@ -19,11 +19,13 @@ import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.systemui.animation.Interpolators.EMPHASIZED;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -31,6 +33,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
@@ -44,7 +47,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
@@ -53,6 +55,9 @@ import java.util.function.Supplier;
 */
 public class TaskbarLauncherStateController {

    private static final String TAG = TaskbarLauncherStateController.class.getSimpleName();
    private static final boolean DEBUG = false;

    public static final int FLAG_RESUMED = 1 << 0;
    public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
    public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
@@ -99,9 +104,13 @@ import java.util.function.Supplier;
                    }
                    updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
                    if (!mShouldDelayLauncherStateAnim) {
                        if (toState == LauncherState.NORMAL) {
                            applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
                        } else {
                            applyState();
                        }
                    }
                }

                @Override
                public void onStateTransitionComplete(LauncherState finalState) {
@@ -122,7 +131,12 @@ import java.util.function.Supplier;
        MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
        mIconAlphaForHome.setConsumer(
                (Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
                alpha -> {
                    mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
                    if (mLauncher.getDeviceProfile().isQsbInline) {
                        mLauncher.getHotseat().setQsbAlpha(alpha > 0 ? 0 : 1);
                    }
                });

        mIconAlignmentForResumedState.finishAnimation();
        onIconAlignmentRatioChangedForAppAndHomeTransition();
@@ -269,6 +283,11 @@ import java.util.function.Supplier;
                ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
                        .animateToValue(toAlignmentForResumedState)
                        .setDuration(duration);
                if (DEBUG) {
                    Log.d(TAG, "mIconAlignmentForResumedState - "
                            + mIconAlignmentForResumedState.value
                            + " -> " + toAlignmentForResumedState + ": " + duration);
                }

                resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
                    @Override
@@ -305,6 +324,11 @@ import java.util.function.Supplier;
                if (isRecentsAnimationRunning) {
                    gestureAlignAnim.setDuration(duration);
                }
                if (DEBUG) {
                    Log.d(TAG, "mIconAlignmentForGestureState - "
                            + mIconAlignmentForGestureState.value
                            + " -> " + toAlignmentForGestureState + ": " + duration);
                }
                gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
@@ -330,6 +354,7 @@ import java.util.function.Supplier;
                    .setDuration(duration));
        }

        animatorSet.setInterpolator(EMPHASIZED);
        if (start) {
            animatorSet.start();
        }
@@ -374,6 +399,12 @@ import java.util.function.Supplier;
            mIconAlignmentForLauncherState.finishAnimation();
            animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
                    .setDuration(duration));
            if (DEBUG) {
                Log.d(TAG, "mIconAlignmentForLauncherState - "
                        + mIconAlignmentForLauncherState.value
                        + " -> " + toAlignment + ": " + duration);
            }
            animatorSet.setInterpolator(EMPHASIZED);
        }
    }

@@ -396,17 +427,17 @@ import java.util.function.Supplier;
        onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
    }

    private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) {
    private void onIconAlignmentRatioChanged(Supplier<AnimatedFloat> alignmentSupplier) {
        if (mControllers == null) {
            return;
        }
        float alignment = alignmentSupplier.get();
        AnimatedFloat animatedFloat = alignmentSupplier.get();
        float currentValue = mIconAlphaForHome.getValue();
        boolean taskbarWillBeVisible = alignment < 1;
        boolean taskbarWillBeVisible = animatedFloat.value < 1;
        boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
                || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);

        updateIconAlignment(alignment);
        updateIconAlignment(animatedFloat.value, animatedFloat.getEndValue());

        // Sync the first frame where we swap taskbar and hotseat.
        if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -416,21 +447,22 @@ import java.util.function.Supplier;
        }
    }

    private void updateIconAlignment(float alignment) {
    private void updateIconAlignment(float alignment, Float endAlignment) {
        mControllers.taskbarViewController.setLauncherIconAlignment(
                alignment, mLauncher.getDeviceProfile());
                alignment, endAlignment, mLauncher.getDeviceProfile());

        // Switch taskbar and hotseat in last frame
        setTaskbarViewVisible(alignment < 1);
        mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment);
    }

    private float getCurrentIconAlignmentRatioBetweenAppAndHome() {
        return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
    private AnimatedFloat getCurrentIconAlignmentRatioBetweenAppAndHome() {
        return mIconAlignmentForResumedState.value > mIconAlignmentForGestureState.value
                ? mIconAlignmentForResumedState : mIconAlignmentForGestureState;
    }

    private float getCurrentIconAlignmentRatioForLauncherState() {
        return mIconAlignmentForLauncherState.value;
    private AnimatedFloat getCurrentIconAlignmentRatioForLauncherState() {
        return mIconAlignmentForLauncherState;
    }

    private void setTaskbarViewVisible(boolean isVisible) {
+45 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,7 @@ import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;

import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -78,6 +80,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
    // Only non-null when device supports having an All Apps button.
    private @Nullable AllAppsButton mAllAppsButton;

    private View mQsb;

    public TaskbarView(@NonNull Context context) {
        this(context, null);
    }
@@ -117,6 +121,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
                    new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
            mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
        }

        // TODO: Disable touch events on QSB otherwise it can crash.
        mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
    }

    private int getColorWithGivenLuminance(int color, float luminance) {
@@ -166,6 +173,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        if (mAllAppsButton != null) {
            removeView(mAllAppsButton);
        }
        removeView(mQsb);

        for (int i = 0; i < hotseatItemInfos.length; i++) {
            ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -242,6 +250,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
            int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
            addView(mAllAppsButton, index);
        }
        if (mActivityContext.getDeviceProfile().isQsbInline) {
            addView(mQsb, Utilities.isRtl(getResources()) ? getChildCount() : 0);
            // Always set QSB to invisible after re-adding.
            mQsb.setVisibility(View.INVISIBLE);
        }

        mThemeIconsBackground = calculateThemeIconsBackground();
        setThemedIconsBackgroundColor(mThemeIconsBackground);
@@ -273,7 +286,12 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int count = getChildCount();
        int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
        int countExcludingQsb = count;
        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
        if (deviceProfile.isQsbInline) {
            countExcludingQsb--;
        }
        int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
        int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
        boolean layoutRtl = isLayoutRtl();
        int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -292,11 +310,26 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
        for (int i = count; i > 0; i--) {
            View child = getChildAt(i - 1);
            if (child == mQsb) {
                int qsbStart;
                int qsbEnd;
                if (layoutRtl) {
                    qsbStart = iconEnd + mItemMarginLeftRight;
                    qsbEnd = qsbStart + deviceProfile.qsbWidth;
                } else {
                    qsbEnd = iconEnd - mItemMarginLeftRight;
                    qsbStart = qsbEnd - deviceProfile.qsbWidth;
                }
                int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2;
                int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight;
                child.layout(qsbStart, qsbTop, qsbEnd, qsbBottom);
            } else {
                iconEnd -= mItemMarginLeftRight;
                int iconStart = iconEnd - mIconTouchSize;
                child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
                iconEnd = iconStart - mItemMarginLeftRight;
            }
        }
        mIconLayoutBounds.left = iconEnd;
    }

@@ -367,6 +400,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
        return mAllAppsButton;
    }

    /**
     * Returns the QSB in the taskbar.
     */
    public View getQsb() {
        return mQsb;
    }

    // FolderIconParent implemented methods.

    @Override
+48 −9
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;

import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -35,12 +36,15 @@ import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiValueAlpha;
@@ -211,9 +215,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
     *                       0 => not aligned
     *                       1 => fully aligned
     */
    public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
    public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
            DeviceProfile launcherDp) {
        if (mIconAlignControllerLazy == null) {
            mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
            mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
        }
        mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
        if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -225,11 +230,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
    /**
     * Creates an animation for aligning the taskbar icons with the provided Launcher device profile
     */
    private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
    private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp,
            Float endAlignment) {
        mOnControllerPreCreateCallback.run();
        PendingAnimation setter = new PendingAnimation(100);
        DeviceProfile taskbarDp = mActivity.getDeviceProfile();
        Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
        float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx;
        float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.iconSizePx;
        int borderSpacing = launcherDp.hotseatBorderSpace;
        int hotseatCellSize = DeviceProfile.calculateCellWidth(
                launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right,
@@ -246,14 +253,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
        }

        int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
        int expandedHeight = Math.max(collapsedHeight,
                mActivity.getDeviceProfile().taskbarSize + offsetY);
        int expandedHeight = Math.max(collapsedHeight, taskbarDp.taskbarSize + offsetY);
        setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
                anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));

        boolean isToHome = endAlignment != null && endAlignment == 1;
        for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
            View child = mTaskbarView.getChildAt(i);

            int positionInHotseat;
            if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
                    && child == mTaskbarView.getAllAppsButtonView()) {
@@ -261,13 +267,46 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
                // as its convenient for animation purposes.
                positionInHotseat = Utilities.isRtl(child.getResources())
                        ? -1
                        : mActivity.getDeviceProfile().numShownHotseatIcons;
                        : taskbarDp.numShownHotseatIcons;

                if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
                    setter.setViewAlpha(child, 0, LINEAR);
                    setter.setViewAlpha(child, 0,
                            isToHome
                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
                                    : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
                }
            } else if (child.getTag() instanceof ItemInfo) {
                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
            } else if (child == mTaskbarView.getQsb()) {
                boolean isRtl = Utilities.isRtl(child.getResources());
                float hotseatIconCenter = isRtl
                        ? launcherDp.widthPx - hotseatPadding.right + borderSpacing
                        + launcherDp.qsbWidth / 2f
                        : hotseatPadding.left - borderSpacing - launcherDp.qsbWidth / 2f;
                float childCenter = (child.getLeft() + child.getRight()) / 2f;
                float halfQsbIconWidthDiff = (launcherDp.qsbWidth - taskbarDp.iconSizePx) / 2f;
                setter.addFloat(child, ICON_TRANSLATE_X,
                        isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
                        hotseatIconCenter - childCenter, LINEAR);

                int qsbContentHeight = child.getHeight() - child.getPaddingTop()
                        - child.getPaddingBottom();
                float scale = ((float) taskbarDp.iconSizePx) / qsbContentHeight;
                setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);

                setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
                        isToHome
                                ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
                                : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
                setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));

                float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.qsbWidth;
                if (child instanceof  HorizontalInsettableView) {
                    setter.addFloat((HorizontalInsettableView) child,
                            HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
                            LINEAR);
                }
                continue;
            } else {
                Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
                continue;
Loading