From 5cf86b263e41cb9def813e166fba0e53c1508551 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 15 Jun 2021 14:49:28 -0700 Subject: [PATCH] Updating taskbar icon alignment state Icon alignment is only tied to Launcher paused/resumed state Creating two separate states for this: 1) Launcher paused/resumed 2) Active gesture interaction (live-titles can affect paused state) Removing state handler dependency on taskbar visibility Bug: 190170303 Bug: 187353581 Bug: 187919439 Test: Manual Change-Id: Ia97cdf43cec1d9213f5dc2af8d66258b34c57514 --- .../taskbar/LauncherTaskbarUIController.java | 141 ++++++++++-------- .../taskbar/TaskbarActivityContext.java | 5 +- .../taskbar/TaskbarStateHandler.java | 23 +-- .../launcher3/taskbar/TaskbarView.java | 53 +------ .../taskbar/TaskbarViewController.java | 77 +++++++++- .../android/quickstep/AbsSwipeUpHandler.java | 3 +- .../quickstep/BaseActivityInterface.java | 3 +- .../quickstep/LauncherActivityInterface.java | 9 +- .../quickstep/TaskAnimationManager.java | 6 + 9 files changed, 177 insertions(+), 143 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index f4168d9f80..255ba1e054 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -15,27 +15,29 @@ */ package com.android.launcher3.taskbar; -import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; -import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_LAUNCHER_STATE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.graphics.Rect; import android.view.MotionEvent; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorListeners; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.RecentsAnimationCallbacks; +import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; +import com.android.quickstep.RecentsAnimationController; +import com.android.systemui.shared.recents.model.ThumbnailData; /** @@ -51,12 +53,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController { final TaskbarDragLayer mTaskbarDragLayer; final TaskbarView mTaskbarView; + private final AnimatedFloat mIconAlignmentForResumedState = + new AnimatedFloat(this::onIconAlignmentRatioChanged); + private final AnimatedFloat mIconAlignmentForGestureState = + new AnimatedFloat(this::onIconAlignmentRatioChanged); + private AnimatedFloat mTaskbarBackgroundAlpha; private AlphaProperty mIconAlphaForHome; - private @Nullable Animator mAnimator; private boolean mIsAnimatingToLauncher; private TaskbarKeyguardController mKeyguardController; + private LauncherState mTargetStateOverride = null; + private TaskbarControllers mControllers; + public LauncherTaskbarUIController( BaseQuickstepLauncher launcher, TaskbarActivityContext context) { mContext = context; @@ -67,7 +76,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mTaskbarStateHandler = mLauncher.getTaskbarStateHandler(); mHotseatController = new TaskbarHotseatController( mLauncher, mTaskbarView::updateHotseatItems); - } @Override @@ -77,21 +85,17 @@ public class LauncherTaskbarUIController extends TaskbarUIController { MultiValueAlpha taskbarIconAlpha = taskbarControllers.taskbarViewController .getTaskbarIconAlpha(); mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME); - mTaskbarStateHandler.setAnimationController(taskbarIconAlpha.getProperty( - ALPHA_INDEX_LAUNCHER_STATE)); + mControllers = taskbarControllers; + mHotseatController.init(); - setTaskbarViewVisible(!mLauncher.hasBeenResumed()); mLauncher.setTaskbarUIController(this); mKeyguardController = taskbarControllers.taskbarKeyguardController; + onLauncherResumedOrPaused(mLauncher.hasBeenResumed()); + mIconAlignmentForResumedState.finishAnimation(); } @Override protected void onDestroy() { - if (mAnimator != null) { - // End this first, in case it relies on properties that are about to be cleaned up. - mAnimator.end(); - } - mTaskbarStateHandler.setAnimationController(null); mHotseatController.cleanup(); setTaskbarViewVisible(true); mLauncher.getHotseat().setIconsAlpha(1f); @@ -100,7 +104,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override protected boolean isTaskbarTouchable() { - return !mIsAnimatingToLauncher; + return !mIsAnimatingToLauncher && mTargetStateOverride == null; } @Override @@ -128,63 +132,82 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } } - long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION; - if (mAnimator != null) { - mAnimator.cancel(); - } - if (isResumed) { - mAnimator = createAnimToLauncher(mLauncher.getStateManager().getState(), duration); - } else { - mAnimator = createAnimToApp(duration); - } - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimator = null; - } - }); - mAnimator.start(); + ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue( + getCurrentIconAlignmentRatio(), isResumed ? 1 : 0) + .setDuration(QuickstepTransitionManager.CONTENT_ALPHA_DURATION); + + anim.addListener(AnimatorListeners.forEndCallback(() -> mIsAnimatingToLauncher = false)); + anim.start(); + mIsAnimatingToLauncher = isResumed; } /** - * Create Taskbar animation when going from an app to Launcher. + * Create Taskbar animation when going from an app to Launcher as part of recents transition. * @param toState If known, the state we will end up in when reaching Launcher. - * TODO: Move this and createAnimToApp to TaskbarStateHandler using the BACKGROUND state + * @param callbacks callbacks to track the recents animation lifecycle. The state change is + * automatically reset once the recents animation finishes */ - public Animator createAnimToLauncher(@NonNull LauncherState toState, long duration) { - PendingAnimation anim = new PendingAnimation(duration); - mTaskbarStateHandler.setState(toState, anim); - - anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 0, LINEAR); - mTaskbarView.alignIconsWithLauncher(mLauncher.getDeviceProfile(), anim); - - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mIsAnimatingToLauncher = true; - } - + public Animator createAnimToLauncher(@NonNull LauncherState toState, + @NonNull RecentsAnimationCallbacks callbacks, + long duration) { + ObjectAnimator animator = mIconAlignmentForGestureState + .animateToValue(mIconAlignmentForGestureState.value, 1) + .setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mIsAnimatingToLauncher = false; - setTaskbarViewVisible(false); + mTargetStateOverride = null; } - }); - return anim.buildAnim(); - } - - private Animator createAnimToApp(long duration) { - PendingAnimation anim = new PendingAnimation(duration); - anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 1, LINEAR); - anim.addListener(AnimatorListeners.forEndCallback(mTaskbarView.resetIconPosition(anim))); - anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - setTaskbarViewVisible(true); + mTargetStateOverride = toState; } }); - return anim.buildAnim(); + callbacks.addListener(new RecentsAnimationListener() { + @Override + public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { + endGestureStateOverride(); + } + + @Override + public void onRecentsAnimationFinished(RecentsAnimationController controller) { + endGestureStateOverride(); + } + + private void endGestureStateOverride() { + callbacks.removeListener(this); + mIconAlignmentForGestureState + .animateToValue(mIconAlignmentForGestureState.value, 0) + .start(); + } + }); + return animator; + } + + private float getCurrentIconAlignmentRatio() { + return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value); + } + + private void onIconAlignmentRatioChanged() { + if (mControllers == null) { + return; + } + float alignment = getCurrentIconAlignmentRatio(); + mControllers.taskbarViewController.setLauncherIconAlignment( + alignment, mLauncher.getDeviceProfile()); + + mTaskbarBackgroundAlpha.updateValue(1 - alignment); + + LauncherState state = mTargetStateOverride != null ? mTargetStateOverride + : mLauncher.getStateManager().getState(); + if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + // If the hotseat icons are visible, then switch taskbar in last frame + setTaskbarViewVisible(alignment < 1); + } else { + mLauncher.getHotseat().setIconsAlpha(1); + mIconAlphaForHome.setValue(1 - alignment); + } } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 000799b73b..106ebe54dc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -17,7 +17,6 @@ package com.android.launcher3.taskbar; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -93,6 +92,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ private final ViewCache mViewCache = new ViewCache(); private final boolean mIsSafeModeEnabled; + private boolean mIsDestroyed = false; public TaskbarActivityContext(Context windowContext, DeviceProfile dp, TaskbarNavButtonController buttonController) { @@ -208,6 +208,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ * Called when this instance of taskbar is no longer needed */ public void onDestroy() { + mIsDestroyed = true; setUIController(TaskbarUIController.DEFAULT); mControllers.onDestroy(); mWindowManager.removeViewImmediate(mDragLayer); @@ -252,7 +253,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset). */ public void setTaskbarWindowHeight(int height) { - if (mWindowLayoutParams.height == height) { + if (mWindowLayoutParams.height == height || mIsDestroyed) { return; } if (height != MATCH_PARENT) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java index 20d4133a97..edd2a22778 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java @@ -18,45 +18,33 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.LauncherState.TASKBAR; import static com.android.launcher3.anim.Interpolators.LINEAR; -import androidx.annotation.Nullable; - import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; -import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SystemUiProxy; /** - * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar - * isn't present (i.e. {@link #setAnimationController} is never called). + * StateHandler to animate Taskbar according to Launcher's state machine. */ public class TaskbarStateHandler implements StateManager.StateHandler { private final BaseQuickstepLauncher mLauncher; - // Contains Taskbar-related properties we should aniamte. If null, don't do anything. - private @Nullable MultiValueAlpha.AlphaProperty mTaskbarAlpha = null; - private AnimatedFloat mNavbarButtonAlpha = new AnimatedFloat(this::updateNavbarButtonAlpha); public TaskbarStateHandler(BaseQuickstepLauncher launcher) { mLauncher = launcher; } - public void setAnimationController(MultiValueAlpha.AlphaProperty taskbarAlpha) { - mTaskbarAlpha = taskbarAlpha; - // Reapply state. - setState(mLauncher.getStateManager().getState()); - updateNavbarButtonAlpha(); - } - @Override public void setState(LauncherState state) { setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER); + // Force update the alpha in case it was not initialized properly + updateNavbarButtonAlpha(); } @Override @@ -69,12 +57,7 @@ public class TaskbarStateHandler implements StateManager.StateHandler mActivityContext.setTaskbarWindowHeight( - mActivityContext.getDeviceProfile().taskbarSize); - } - private void removeAndRecycle(View view) { removeView(view); view.setOnClickListener(null); @@ -195,6 +143,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar // so if the info changes we need to reinflate. This should only happen if a new // folder is dragged to the position that another folder previously existed. removeAndRecycle(hotseatView); + hotseatView = null; } else { // View found break; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 10cc926759..c7ac4a4f5b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -15,19 +15,29 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.graphics.Rect; import android.view.View; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.MultiValueAlpha; /** * Handles properties/data collection, then passes the results to TaskbarView to render. */ public class TaskbarViewController { + private static final Runnable NO_OP = () -> { }; public static final int ALPHA_INDEX_HOME = 0; - public static final int ALPHA_INDEX_LAUNCHER_STATE = 1; - public static final int ALPHA_INDEX_IME = 2; - public static final int ALPHA_INDEX_KEYGUARD = 3; + public static final int ALPHA_INDEX_IME = 1; + public static final int ALPHA_INDEX_KEYGUARD = 2; private final TaskbarActivityContext mActivity; private final TaskbarView mTaskbarView; @@ -36,10 +46,15 @@ public class TaskbarViewController { // Initialized in init. private TaskbarControllers mControllers; + // Animation to align icons with Launcher, created lazily. This allows the controller to be + // active only during the animation and does not need to worry about layout changes. + private AnimatorPlaybackController mIconAlignControllerLazy = null; + private Runnable mOnControllerPreCreateCallback = NO_OP; + public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) { mActivity = activity; mTaskbarView = taskbarView; - mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4); + mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 3); mTaskbarIconAlpha.setUpdateVisibility(true); } @@ -71,6 +86,60 @@ public class TaskbarViewController { mTaskbarView.setClickAndLongClickListenersForIcon(icon); } + /** + * Sets the taskbar icon alignment relative to Launcher hotseat icons + * @param alignmentRatio [0, 1] + * 0 => not aligned + * 1 => fully aligned + */ + public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { + if (mIconAlignControllerLazy == null) { + mIconAlignControllerLazy = createIconAlignmentController(launcherDp); + } + mIconAlignControllerLazy.setPlayFraction(alignmentRatio); + if (alignmentRatio <= 0 || alignmentRatio >= 1) { + // Cleanup lazy controller so that it is created again in next animation + mIconAlignControllerLazy = null; + } + } + + /** + * Creates an animation for aligning the taskbar icons with the provided Launcher device profile + */ + private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) { + mOnControllerPreCreateCallback.run(); + PendingAnimation setter = new PendingAnimation(100); + Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity); + float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx; + int hotseatCellSize = + (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right) + / launcherDp.numShownHotseatIcons; + + int offsetY = launcherDp.getTaskbarOffsetY(); + setter.setFloat(mTaskbarView, VIEW_TRANSLATE_Y, -offsetY, LINEAR); + + int collapsedHeight = mActivity.getDeviceProfile().taskbarSize; + int expandedHeight = collapsedHeight + offsetY; + setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight( + anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight)); + + int count = mTaskbarView.getChildCount(); + for (int i = 0; i < count; i++) { + View child = mTaskbarView.getChildAt(i); + ItemInfo info = (ItemInfo) child.getTag(); + setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR); + + float childCenter = (child.getLeft() + child.getRight()) / 2; + float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId + + hotseatCellSize / 2; + setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR); + } + + AnimatorPlaybackController controller = setter.createPlaybackController(); + mOnControllerPreCreateCallback = () -> controller.setPlayFraction(0); + return controller; + } + /** * Callbacks for {@link TaskbarView} to interact with its controller. */ diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 0ebaea2234..a8658a788e 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1107,7 +1107,8 @@ public abstract class AbsSwipeUpHandler, mActivityRestartListener); mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher( - mGestureState.getEndTarget(), duration); + mGestureState.getEndTarget(), duration, + mTaskAnimationManager.getCurrentCallbacks()); if (mParallelRunningAnim != null) { mParallelRunningAnim.start(); } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index a45ced4b21..2699b0795b 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -346,7 +346,8 @@ public abstract class BaseActivityInterface