diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index e1a3b729c1..b20752d630 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -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())); - viewsToAnimate.add(mLauncher.getHotseat()); + // 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); diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index ca30e72609..6df31e5670 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -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 diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index dc0ef27ba1..591b32f783 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -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,7 +104,11 @@ import java.util.function.Supplier; } updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true); if (!mShouldDelayLauncherStateAnim) { - applyState(); + if (toState == LauncherState.NORMAL) { + applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION); + } else { + applyState(); + } } } @@ -122,7 +131,12 @@ import java.util.function.Supplier; MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha(); mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME); mIconAlphaForHome.setConsumer( - (Consumer) 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 alignmentSupplier) { + private void onIconAlignmentRatioChanged(Supplier 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) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 6f88d649fb..6a43cc5f4b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -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,10 +310,25 @@ 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); - iconEnd -= mItemMarginLeftRight; - int iconStart = iconEnd - mIconTouchSize; - child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom); - iconEnd = iconStart - mItemMarginLeftRight; + 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 diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index fdd9de5db3..0cbd0d1c8e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -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; diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 713fa25383..b3e2d911cc 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -234,7 +234,6 @@ public abstract class AbsSwipeUpHandler, STATE_LAUNCHER_BIND_TO_SERVICE; public static final long MAX_SWIPE_DURATION = 350; - public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS; public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; private static final float SWIPE_DURATION_MULTIPLIER = @@ -1141,7 +1140,9 @@ public abstract class AbsSwipeUpHandler, mInputConsumerProxy.enable(); } if (endTarget == HOME) { - duration = HOME_DURATION; + duration = mActivity.getDeviceProfile().isTaskbarPresent + ? StaggeredWorkspaceAnim.DURATION_TASKBAR_MS + : StaggeredWorkspaceAnim.DURATION_MS; // Early detach the nav bar once the endTarget is determined as HOME if (mRecentsAnimationController != null) { mRecentsAnimationController.detachNavigationBarFromApp(true); diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java index 6c7a885a1f..a1665530a3 100644 --- a/quickstep/src/com/android/quickstep/AnimatedFloat.java +++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java @@ -133,4 +133,11 @@ public class AnimatedFloat { public boolean isAnimatingToValue(float endValue) { return isAnimating() && mEndValue != null && mEndValue == endValue; } + + /** + * Returns the value we are animating to, or {@code null} if we are not currently animating. + */ + public Float getEndValue() { + return mEndValue; + } } diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index 32e08ffa8d..de527a7d0c 100644 --- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -41,6 +41,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Workspace; @@ -60,10 +61,10 @@ import com.android.systemui.plugins.ResourceProvider; public class StaggeredWorkspaceAnim { private static final int APP_CLOSE_ROW_START_DELAY_MS = 10; - // How long it takes to fade in each staggered row. - private static final int ALPHA_DURATION_MS = 250; // Should be used for animations running alongside this StaggeredWorkspaceAnim. public static final int DURATION_MS = 250; + public static final int DURATION_TASKBAR_MS = + QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION; private static final float MAX_VELOCITY_PX_PER_S = 22f; @@ -91,16 +92,20 @@ public class StaggeredWorkspaceAnim { mSpringTransY = transFactor * launcher.getResources() .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y); + DeviceProfile grid = launcher.getDeviceProfile(); + long duration = grid.isTaskbarPresent ? DURATION_TASKBAR_MS : DURATION_MS; if (staggerWorkspace) { - DeviceProfile grid = launcher.getDeviceProfile(); Workspace workspace = launcher.getWorkspace(); Hotseat hotseat = launcher.getHotseat(); - // Hotseat and QSB takes up two additional rows. - int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2); + boolean staggerHotseat = !grid.isVerticalBarLayout() && !grid.isTaskbarPresent; + boolean staggerQsb = + !grid.isVerticalBarLayout() && !(grid.isTaskbarPresent && grid.isQsbInline); + int totalRows = grid.inv.numRows + (staggerHotseat ? 1 : 0) + (staggerQsb ? 1 : 0); // Add animation for all the visible workspace pages - workspace.forEachVisiblePage(page -> addAnimationForPage((CellLayout) page, totalRows)); + workspace.forEachVisiblePage( + page -> addAnimationForPage((CellLayout) page, totalRows, duration)); boolean workspaceClipChildren = workspace.getClipChildren(); boolean workspaceClipToPadding = workspace.getClipToPadding(); @@ -119,23 +124,34 @@ public class StaggeredWorkspaceAnim { View child = hotseatIcons.getChildAt(i); CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams()); - addStaggeredAnimationForView(child, lp.cellY + 1, totalRows); + addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration); } } else { final int hotseatRow, qsbRow; if (grid.isTaskbarPresent) { - qsbRow = grid.inv.numRows + 1; - hotseatRow = grid.inv.numRows + 2; + if (grid.isQsbInline) { + qsbRow = grid.inv.numRows + 1; + hotseatRow = grid.inv.numRows + 1; + } else { + qsbRow = grid.inv.numRows + 1; + hotseatRow = grid.inv.numRows + 2; + } } else { hotseatRow = grid.inv.numRows + 1; qsbRow = grid.inv.numRows + 2; } - for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { - View child = hotseatIcons.getChildAt(i); - addStaggeredAnimationForView(child, hotseatRow, totalRows); - } - addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows); + // Do not stagger hotseat as a whole when taskbar is present, and stagger QSB only + // if it's not inline. + if (staggerHotseat) { + for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { + View child = hotseatIcons.getChildAt(i); + addStaggeredAnimationForView(child, hotseatRow, totalRows, duration); + } + } + if (staggerQsb) { + addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows, duration); + } } mAnimators.addListener(new AnimatorListenerAdapter() { @@ -153,19 +169,19 @@ public class StaggeredWorkspaceAnim { mAnimators.addListener(forEndCallback(launcher::resumeExpensiveViewUpdates)); if (animateOverviewScrim) { - PendingAnimation pendingAnimation = new PendingAnimation(DURATION_MS); + PendingAnimation pendingAnimation = new PendingAnimation(duration); launcher.getWorkspace().getStateTransitionAnimation() .setScrim(pendingAnimation, NORMAL, new StateAnimationConfig()); mAnimators.play(pendingAnimation.buildAnim()); } - addDepthAnimationForState(launcher, NORMAL, DURATION_MS); + addDepthAnimationForState(launcher, NORMAL, duration); mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f) - .setDuration(DURATION_MS)); + .setDuration(duration)); } - private void addAnimationForPage(CellLayout page, int totalRows) { + private void addAnimationForPage(CellLayout page, int totalRows, long duration) { ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets(); boolean pageClipChildren = page.getClipChildren(); @@ -178,7 +194,7 @@ public class StaggeredWorkspaceAnim { for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) { View child = itemsContainer.getChildAt(i); CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams()); - addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows); + addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration); } mAnimators.addListener(new AnimatorListenerAdapter() { @@ -231,8 +247,9 @@ public class StaggeredWorkspaceAnim { * @param v A view on the workspace. * @param row The bottom-most row that contains the view. * @param totalRows Total number of rows. + * @param duration duration of the animation */ - private void addStaggeredAnimationForView(View v, int row, int totalRows) { + private void addStaggeredAnimationForView(View v, int row, int totalRows, long duration) { if (mIgnoredView != null && mIgnoredView == v) return; // Invert the rows, because we stagger starting from the bottom of the screen. int invertedRow = totalRows - row; @@ -266,7 +283,7 @@ public class StaggeredWorkspaceAnim { v.setAlpha(0); ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f); alpha.setInterpolator(LINEAR); - alpha.setDuration(ALPHA_DURATION_MS); + alpha.setDuration(duration); alpha.setStartDelay(startDelay); alpha.addListener(new AnimatorListenerAdapter() { @Override diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 76106fc58d..8c4c66277c 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -206,6 +206,13 @@ public class Hotseat extends CellLayout implements Insettable { getShortcutsAndWidgets().setAlpha(alpha); } + /** + * Sets the alpha value of just our QSB. + */ + public void setQsbAlpha(float alpha) { + mQsb.setAlpha(alpha); + } + public float getIconsAlpha() { return getShortcutsAndWidgets().getAlpha(); } diff --git a/src/com/android/launcher3/util/HorizontalInsettableView.java b/src/com/android/launcher3/util/HorizontalInsettableView.java index 7979bc0f25..486b73dd5f 100644 --- a/src/com/android/launcher3/util/HorizontalInsettableView.java +++ b/src/com/android/launcher3/util/HorizontalInsettableView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.util; +import android.util.FloatProperty; + /** * Allows the implementing view to add insets to the left and right. */ @@ -32,4 +34,22 @@ public interface HorizontalInsettableView { */ void setHorizontalInsets(float insetPercentage); + /** + * Returns the width percentage to inset the content from the left and from the right. See + * {@link #setHorizontalInsets}; + */ + float getHorizontalInsets(); + + FloatProperty HORIZONTAL_INSETS = + new FloatProperty("horizontalInsets") { + @Override + public Float get(HorizontalInsettableView view) { + return view.getHorizontalInsets(); + } + + @Override + public void setValue(HorizontalInsettableView view, float insetPercentage) { + view.setHorizontalInsets(insetPercentage); + } + }; }