From 9ce3b25ef6f30418558da49efead328d5353210b Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 22 Mar 2021 14:43:58 -0700 Subject: [PATCH] Add TaskbarView as a child of Hotseat Instead of keeping the Taskbar window showing above the home screen, we now integrate a copy of TaskbarView directly into Launcher's view hierarchy. Most TaskbarController calls apply to both TaskbarViews (mTaskbarViewInApp and mTaskbarViewOnHome), except for calls related to Hotseat, which only apply to mTasbkarViewInApp given the real Hotseat will be showing with mTaskbarViewOnHome. More cleanup will follow this change. Test: All taskbar interactions (e.g. drag and drop) continue to work Bug: 182512211 Bug: 171917176 Change-Id: I0f0b124f652daa85f866e1df8f9e2981540331a0 --- quickstep/res/layout/taskbar_divider.xml | 2 - quickstep/res/layout/taskbar_view.xml | 26 ++++ .../launcher3/BaseQuickstepLauncher.java | 4 +- .../taskbar/TaskbarActivityContext.java | 5 +- .../taskbar/TaskbarContainerView.java | 2 +- .../launcher3/taskbar/TaskbarController.java | 145 +++++++++++------- .../launcher3/taskbar/TaskbarView.java | 91 +++++++---- .../util/StaggeredWorkspaceAnim.java | 26 +++- res/layout/taskbar_view.xml | 21 +++ src/com/android/launcher3/Hotseat.java | 44 ++++-- src/com/android/launcher3/LauncherState.java | 7 +- .../WorkspaceStateTransitionAnimation.java | 5 +- .../launcher3/views/ActivityContext.java | 18 ++- 13 files changed, 264 insertions(+), 132 deletions(-) create mode 100644 quickstep/res/layout/taskbar_view.xml create mode 100644 res/layout/taskbar_view.xml diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml index 6e1aa1e3d5..87649f752e 100644 --- a/quickstep/res/layout/taskbar_divider.xml +++ b/quickstep/res/layout/taskbar_divider.xml @@ -18,6 +18,4 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/taskbar_divider_thickness" android:layout_height="@dimen/taskbar_divider_height" - android:layout_marginStart="@dimen/taskbar_icon_spacing" - android:layout_marginEnd="@dimen/taskbar_icon_spacing" android:background="@color/taskbar_divider" /> \ No newline at end of file diff --git a/quickstep/res/layout/taskbar_view.xml b/quickstep/res/layout/taskbar_view.xml new file mode 100644 index 0000000000..34a88ea6a2 --- /dev/null +++ b/quickstep/res/layout/taskbar_view.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index c4becf105f..9ab49cebfa 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -46,6 +46,7 @@ import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarController; import com.android.launcher3.taskbar.TaskbarStateHandler; +import com.android.launcher3.taskbar.TaskbarView; import com.android.launcher3.uioverrides.RecentsViewStateController; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.DisplayController; @@ -243,9 +244,10 @@ public abstract class BaseQuickstepLauncher extends Launcher mTaskbarController = null; } if (mDeviceProfile.isTaskbarPresent) { + TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView(); TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this); mTaskbarController = new TaskbarController(this, - taskbarActivityContext.getTaskbarContainerView()); + taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome); mTaskbarController.init(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 8312b82062..fc5e2c1740 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -54,10 +54,7 @@ public class TaskbarActivityContext extends ContextWrapper implements ActivityCo return mTaskbarContainerView; } - /** - * @return A LayoutInflater to use in this Context. Views inflated with this LayoutInflater will - * be able to access this TaskbarActivityContext via ActivityContext.lookupContext(). - */ + @Override public LayoutInflater getLayoutInflater() { return mLayoutInflater; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java index 528f43e699..5202d91684 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java @@ -84,7 +84,7 @@ public class TaskbarContainerView extends BaseDragLayer private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() { return insetsInfo -> { if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD - || mTaskbarView.isDraggingItem()) { + || mTaskbarView.getVisibility() != VISIBLE || mTaskbarView.isDraggingItem()) { // We're invisible or dragging out of taskbar, let touches pass through us. insetsInfo.touchableRegion.setEmpty(); insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index f652961629..abf6d54146 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -19,8 +19,6 @@ 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 com.android.launcher3.AbstractFloatingView.TYPE_HIDE_TASKBAR; -import static com.android.launcher3.AbstractFloatingView.TYPE_REPLACE_TASKBAR_WITH_HOTSEAT; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -43,7 +41,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Hotseat; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; @@ -54,6 +51,7 @@ import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.views.ActivityContext; import com.android.quickstep.AnimatedFloat; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -70,7 +68,8 @@ public class TaskbarController { private static final String WINDOW_TITLE = "Taskbar"; private final TaskbarContainerView mTaskbarContainerView; - private final TaskbarView mTaskbarView; + private final TaskbarView mTaskbarViewInApp; + private final TaskbarView mTaskbarViewOnHome; private final BaseQuickstepLauncher mLauncher; private final WindowManager mWindowManager; // Layout width and height of the Taskbar in the default state. @@ -91,14 +90,17 @@ public class TaskbarController { private @Nullable Animator mAnimator; private boolean mIsAnimatingToLauncher; + private boolean mIsAnimatingToApp; public TaskbarController(BaseQuickstepLauncher launcher, - TaskbarContainerView taskbarContainerView) { + TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) { mLauncher = launcher; mTaskbarContainerView = taskbarContainerView; mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks()); - mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view); - mTaskbarView.construct(createTaskbarViewCallbacks()); + mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view); + mTaskbarViewInApp.construct(createTaskbarViewCallbacks()); + mTaskbarViewOnHome = taskbarViewOnHome; + mTaskbarViewOnHome.construct(createTaskbarViewCallbacks()); mWindowManager = mLauncher.getWindowManager(); mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize); mTaskbarStateHandler = mLauncher.getTaskbarStateHandler(); @@ -115,12 +117,13 @@ public class TaskbarController { return new TaskbarVisibilityControllerCallbacks() { @Override public void updateTaskbarBackgroundAlpha(float alpha) { - mTaskbarView.setBackgroundAlpha(alpha); + mTaskbarViewInApp.setBackgroundAlpha(alpha); } @Override public void updateTaskbarVisibilityAlpha(float alpha) { mTaskbarContainerView.setAlpha(alpha); + mTaskbarViewOnHome.setAlpha(alpha); } }; } @@ -196,7 +199,7 @@ public class TaskbarController { public View.OnLongClickListener getItemOnLongClickListener() { return view -> { if (mLauncher.hasBeenResumed() && view.getTag() instanceof ItemInfo) { - alignRealHotseatWithTaskbar(); + // TODO: remove this path return mDragController.startWorkspaceDragOnLongClick(view); } else { return mDragController.startSystemDragOnLongClick(view); @@ -205,11 +208,24 @@ public class TaskbarController { } @Override - public int getEmptyHotseatViewVisibility() { + public int getEmptyHotseatViewVisibility(TaskbarView taskbarView) { // When on the home screen, we want the empty hotseat views to take up their full // space so that the others line up with the home screen hotseat. - return mLauncher.hasBeenResumed() || mIsAnimatingToLauncher - ? View.INVISIBLE : View.GONE; + boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome + || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher; + return isOnHomeScreen ? View.INVISIBLE : View.GONE; + } + + @Override + public float getNonIconScale(TaskbarView taskbarView) { + return taskbarView == mTaskbarViewOnHome ? getTaskbarScaleOnHome() : 1f; + } + + @Override + public void onItemPositionsChanged(TaskbarView taskbarView) { + if (taskbarView == mTaskbarViewOnHome) { + alignRealHotseatWithTaskbar(); + } } }; } @@ -218,7 +234,7 @@ public class TaskbarController { return new TaskbarHotseatControllerCallbacks() { @Override public void updateHotseatItems(ItemInfo[] hotseatItemInfos) { - mTaskbarView.updateHotseatItems(hotseatItemInfos); + mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos); mLatestLoadedHotseatItems = hotseatItemInfos; dedupeAndUpdateRecentItems(); } @@ -235,7 +251,8 @@ public class TaskbarController { @Override public void updateRecentTaskAtIndex(int taskIndex, Task task) { - mTaskbarView.updateRecentTaskAtIndex(taskIndex, task); + mTaskbarViewInApp.updateRecentTaskAtIndex(taskIndex, task); + mTaskbarViewOnHome.updateRecentTaskAtIndex(taskIndex, task); } }; } @@ -244,16 +261,20 @@ public class TaskbarController { * Initializes the Taskbar, including adding it to the screen. */ public void init() { - mTaskbarView.init(mHotseatController.getNumHotseatIcons(), + mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mRecentsController.getNumRecentIcons()); - mTaskbarContainerView.init(mTaskbarView); + mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), + mRecentsController.getNumRecentIcons()); + mTaskbarContainerView.init(mTaskbarViewInApp); addToWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks()); mTaskbarVisibilityController.init(); mHotseatController.init(); mRecentsController.init(); - SCALE_PROPERTY.set(mTaskbarView, mLauncher.hasBeenResumed() ? getTaskbarScaleOnHome() : 1f); + SCALE_PROPERTY.set(mTaskbarViewInApp, mLauncher.hasBeenResumed() + ? getTaskbarScaleOnHome() : 1f); + updateWhichTaskbarViewIsVisible(); } private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() { @@ -274,7 +295,8 @@ public class TaskbarController { mAnimator.end(); } - mTaskbarView.cleanup(); + mTaskbarViewInApp.cleanup(); + mTaskbarViewOnHome.cleanup(); mTaskbarContainerView.cleanup(); removeFromWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(null); @@ -313,7 +335,7 @@ public class TaskbarController { TaskbarContainerView.LayoutParams taskbarLayoutParams = new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y); taskbarLayoutParams.gravity = gravity; - mTaskbarView.setLayoutParams(taskbarLayoutParams); + mTaskbarViewInApp.setLayoutParams(taskbarLayoutParams); mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams); } @@ -330,7 +352,6 @@ public class TaskbarController { mAnimator = createAnimToLauncher(null, duration); } else { mAnimator = createAnimToApp(duration); - replaceTaskbarWithHotseatOrViceVersa(); } mAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -351,36 +372,41 @@ public class TaskbarController { if (toState != null) { mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim); } - anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(), + anim.addFloat(mTaskbarViewInApp, SCALE_PROPERTY, mTaskbarViewInApp.getScaleX(), getTaskbarScaleOnHome(), LINEAR); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mIsAnimatingToLauncher = true; - mTaskbarView.updateHotseatItemsVisibility(); + mTaskbarViewInApp.updateHotseatItemsVisibility(); } @Override public void onAnimationEnd(Animator animation) { mIsAnimatingToLauncher = false; + updateWhichTaskbarViewIsVisible(); } }); - anim.addOnFrameCallback(this::alignRealHotseatWithTaskbar); - return anim.buildAnim(); } private Animator createAnimToApp(long duration) { PendingAnimation anim = new PendingAnimation(duration); anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration)); - anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(), 1f, LINEAR); + anim.addFloat(mTaskbarViewInApp, SCALE_PROPERTY, mTaskbarViewInApp.getScaleX(), 1f, LINEAR); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mTaskbarView.updateHotseatItemsVisibility(); - setReplaceTaskbarWithHotseat(false); + mIsAnimatingToApp = true; + mTaskbarViewInApp.updateHotseatItemsVisibility(); + updateWhichTaskbarViewIsVisible(); + } + + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToApp = false; } }); return anim.buildAnim(); @@ -405,11 +431,11 @@ public class TaskbarController { * @return Whether any Taskbar item could handle the given MotionEvent if given the chance. */ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) { - return mTaskbarView.isEventOverAnyItem(ev); + return mTaskbarViewInApp.isEventOverAnyItem(ev); } public boolean isDraggingItem() { - return mTaskbarView.isDraggingItem(); + return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem(); } private void dedupeAndUpdateRecentItems() { @@ -454,7 +480,8 @@ public class TaskbarController { tasksArray[tasksArray.length - 1 - i] = task; } - mTaskbarView.updateRecentTasks(tasksArray); + mTaskbarViewInApp.updateRecentTasks(tasksArray); + mTaskbarViewOnHome.updateRecentTasks(tasksArray); mRecentsController.loadIconsForTasks(tasksArray); } @@ -475,10 +502,11 @@ public class TaskbarController { int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize; int hotseatTopDiff = hotseatHeight - grid.taskbarSize; - mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds); - mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top + hotseatTopDiff, - mTaskbarView.getWidth() - hotseatBounds.right, - mTaskbarView.getHeight() - hotseatBounds.bottom); + mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds); + mLauncher.getHotseat().setPadding(hotseatBounds.left, + hotseatBounds.top + hotseatTopDiff, + mTaskbarViewOnHome.getWidth() - hotseatBounds.right, + mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom); } /** @@ -486,36 +514,32 @@ public class TaskbarController { * show the real one instead. */ public void onLauncherDragLayerHierarchyChanged() { - replaceTaskbarWithHotseatOrViceVersa(); + // TODO: remove, as this is a no-op now } - private void replaceTaskbarWithHotseatOrViceVersa() { - boolean replaceTaskbarWithHotseat = AbstractFloatingView.getTopOpenViewWithType(mLauncher, - TYPE_REPLACE_TASKBAR_WITH_HOTSEAT) != null; - if (!mLauncher.hasBeenResumed()) { - replaceTaskbarWithHotseat = false; - } - setReplaceTaskbarWithHotseat(replaceTaskbarWithHotseat); - - boolean hideTaskbar = AbstractFloatingView.getTopOpenViewWithType(mLauncher, - TYPE_HIDE_TASKBAR) != null; - mTaskbarVisibilityController.animateToVisibilityForFloatingView(hideTaskbar ? 0f : 1f); - } - - private void setReplaceTaskbarWithHotseat(boolean replaceTaskbarWithHotseat) { - Hotseat hotseat = mLauncher.getHotseat(); - if (replaceTaskbarWithHotseat) { - alignRealHotseatWithTaskbar(); - hotseat.getReplaceTaskbarAlpha().setValue(1f); - mTaskbarView.setHotseatViewsHidden(true); + private void updateWhichTaskbarViewIsVisible() { + boolean isInApp = !mLauncher.hasBeenResumed() || mIsAnimatingToLauncher + || mIsAnimatingToApp; + if (isInApp) { + mTaskbarViewInApp.setVisibility(View.VISIBLE); + mTaskbarViewOnHome.setVisibility(View.INVISIBLE); + mLauncher.getHotseat().setIconsAlpha(0); } else { - hotseat.getReplaceTaskbarAlpha().setValue(0f); - mTaskbarView.setHotseatViewsHidden(false); + mTaskbarViewInApp.setVisibility(View.INVISIBLE); + mTaskbarViewOnHome.setVisibility(View.VISIBLE); + mLauncher.getHotseat().setIconsAlpha(1); } } - private float getTaskbarScaleOnHome() { - return 1f / mTaskbarContainerView.getTaskbarActivityContext().getTaskbarIconScale(); + /** + * Returns the ratio of the taskbar icon size on home vs in an app. + */ + public float getTaskbarScaleOnHome() { + DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext() + .getDeviceProfile(); + DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext()) + .getDeviceProfile(); + return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx; } /** @@ -561,7 +585,10 @@ public class TaskbarController { protected interface TaskbarViewCallbacks { View.OnClickListener getItemOnClickListener(); View.OnLongClickListener getItemOnLongClickListener(); - int getEmptyHotseatViewVisibility(); + int getEmptyHotseatViewVisibility(TaskbarView taskbarView); + /** Returns how much to scale non-icon elements such as spacing and dividers. */ + float getNonIconScale(TaskbarView taskbarView); + void onItemPositionsChanged(TaskbarView taskbarView); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 1d762e9696..3567c17afa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -15,11 +15,14 @@ */ package com.android.launcher3.taskbar; +import android.animation.Animator; +import android.animation.AnimatorSet; import android.animation.LayoutTransition; +import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Matrix; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -36,6 +39,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.folder.FolderIcon; @@ -48,20 +52,23 @@ import com.android.systemui.shared.recents.model.Task; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. */ -public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent { +public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable { private final ColorDrawable mBackgroundDrawable; - private final int mItemMarginLeftRight; + private final int mDividerWidth; + private final int mDividerHeight; private final int mIconTouchSize; private final boolean mIsRtl; private final int mTouchSlop; private final RectF mTempDelegateBounds = new RectF(); private final RectF mDelegateSlopBounds = new RectF(); private final int[] mTempOutLocation = new int[2]; - private final Matrix mTempMatrix = new Matrix(); // Initialized in TaskbarController constructor. private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; + // Scale on elements that aren't icons. + private float mNonIconScale; + private int mItemMarginLeftRight; // Initialized in init(). private LayoutTransition mLayoutTransition; @@ -78,7 +85,6 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa private boolean mIsDraggingItem; // Only non-null when the corresponding Folder is open. private @Nullable FolderIcon mLeaveBehindFolderIcon; - private boolean mIsHotseatHidden; public TaskbarView(@NonNull Context context) { this(context, null); @@ -99,7 +105,8 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa Resources resources = getResources(); mBackgroundDrawable = (ColorDrawable) getBackground(); - mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing); + mDividerWidth = resources.getDimensionPixelSize(R.dimen.taskbar_divider_thickness); + mDividerHeight = resources.getDimensionPixelSize(R.dimen.taskbar_divider_height); mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size); mIsRtl = Utilities.isRtl(resources); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); @@ -107,11 +114,16 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) { mControllerCallbacks = taskbarViewCallbacks; + mNonIconScale = mControllerCallbacks.getNonIconScale(this); + mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing); + mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale); } protected void init(int numHotseatIcons, int numRecentIcons) { mLayoutTransition = new LayoutTransition(); - setLayoutTransitionsEnabled(true); + addUpdateListenerForAllLayoutTransitions( + () -> mControllerCallbacks.onItemPositionsChanged(this)); + setLayoutTransition(mLayoutTransition); mHotseatStartIndex = 0; mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1; @@ -125,12 +137,30 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa updateRecentTasks(new Task[numRecentIcons]); } - private void setLayoutTransitionsEnabled(boolean enabled) { - setLayoutTransition(enabled ? mLayoutTransition : null); + private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) { + addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate); + addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate); + addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate); + addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate); + addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate); + } + + private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) { + Animator anim = mLayoutTransition.getAnimator(transitionType); + if (anim instanceof ValueAnimator) { + ((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run()); + } else { + AnimatorSet animSet = new AnimatorSet(); + ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1); + updateAnim.addUpdateListener(valueAnimator -> onUpdate.run()); + animSet.playTogether(anim, updateAnim); + mLayoutTransition.setAnimator(transitionType, animSet); + } } protected void cleanup() { removeAllViews(); + mHotseatRecentsDivider = null; } /** @@ -170,12 +200,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId || needsReinflate) { removeView(hotseatView); - TaskbarActivityContext activityContext = - ActivityContext.lookupContext(getContext()); + ActivityContext activityContext = ActivityContext.lookupContext(getContext()); if (isFolder) { FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, - activityContext, this, folderInfo); + ActivityContext.lookupContext(getContext()), this, folderInfo); folderIcon.setTextVisible(false); hotseatView = folderIcon; } else { @@ -216,22 +245,12 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } } - /** - * Hides or shows the hotseat items immediately (without layout transitions). - */ - protected void setHotseatViewsHidden(boolean hidden) { - mIsHotseatHidden = hidden; - setLayoutTransitionsEnabled(false); - updateHotseatItemsVisibility(); - setLayoutTransitionsEnabled(true); - } - private void updateHotseatItemVisibility(View hotseatView) { if (hotseatView.getTag() != null) { - hotseatView.setVisibility(mIsHotseatHidden ? INVISIBLE : VISIBLE); + hotseatView.setVisibility(VISIBLE); } else { int oldVisibility = hotseatView.getVisibility(); - int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(); + int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this); hotseatView.setVisibility(newVisibility); if (oldVisibility == GONE && newVisibility != GONE) { // By default, the layout transition only runs when going to VISIBLE, @@ -243,7 +262,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa private View addDivider(int dividerIndex) { View divider = inflate(R.layout.taskbar_divider); - addView(divider, dividerIndex); + LayoutParams lp = new LayoutParams(mDividerWidth, mDividerHeight); + lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); + divider.setScaleX(mNonIconScale); + divider.setScaleY(mNonIconScale); + addView(divider, dividerIndex, lp); return divider; } @@ -437,9 +460,9 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } /** - * @return The bounding box of where the hotseat elements will be when we reach the given scale. + * @return The bounding box of where the hotseat elements are relative to this TaskbarView. */ - protected RectF getHotseatBoundsAtScale(float taskbarViewScale) { + protected RectF getHotseatBounds() { View firstHotseatView = null, lastHotseatView = null; for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { View child = getChildAt(i); @@ -455,14 +478,11 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView; View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView; - RectF hotseatBounds = new RectF( + return new RectF( leftmostHotseatView.getLeft() - mItemMarginLeftRight, leftmostHotseatView.getTop(), rightmostHotseatView.getRight() + mItemMarginLeftRight, rightmostHotseatView.getBottom()); - mTempMatrix.setScale(taskbarViewScale, taskbarViewScale, getPivotX(), getPivotY()); - mTempMatrix.mapRect(hotseatBounds); - return hotseatBounds; } // FolderIconParent implemented methods. @@ -493,7 +513,12 @@ public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconPa } private View inflate(@LayoutRes int layoutResId) { - TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext()); - return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false); + return ActivityContext.lookupContext(getContext()).getLayoutInflater() + .inflate(layoutResId, this, false); + } + + @Override + public void setInsets(Rect insets) { + // Ignore, we just implement Insettable to draw behind system insets. } } diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index ae644cd6fd..3faf72a006 100644 --- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -112,20 +112,32 @@ public class StaggeredWorkspaceAnim { } // Set up springs for the hotseat and qsb. - ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0); + ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets(); if (grid.isVerticalBarLayout()) { - for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) { - View child = hotseatChild.getChildAt(i); + for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { + View child = hotseatIcons.getChildAt(i); CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams()); addStaggeredAnimationForView(child, lp.cellY + 1, totalRows); } } else { - for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) { - View child = hotseatChild.getChildAt(i); - addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows); + final int hotseatRow, qsbRow, taskbarRow; + if (grid.isTaskbarPresent) { + qsbRow = grid.inv.numRows + 1; + hotseatRow = grid.inv.numRows + 2; + } else { + hotseatRow = grid.inv.numRows + 1; + qsbRow = grid.inv.numRows + 2; + } + // Taskbar and hotseat overlap. + taskbarRow = hotseatRow; + + for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { + View child = hotseatIcons.getChildAt(i); + addStaggeredAnimationForView(child, hotseatRow, totalRows); } - addStaggeredAnimationForView(hotseat.getQsb(), grid.inv.numRows + 2, totalRows); + addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows); + addStaggeredAnimationForView(hotseat.getTaskbarView(), taskbarRow, totalRows); } if (animateOverviewScrim) { diff --git a/res/layout/taskbar_view.xml b/res/layout/taskbar_view.xml new file mode 100644 index 0000000000..96ae43db4a --- /dev/null +++ b/res/layout/taskbar_view.xml @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index af4a843b12..4049ed6882 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -29,8 +29,6 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; -import com.android.launcher3.util.MultiValueAlpha; - import java.util.function.Consumer; /** @@ -38,10 +36,6 @@ import java.util.function.Consumer; */ public class Hotseat extends CellLayout implements Insettable { - private static final int ALPHA_INDEX_STATE = 0; - private static final int ALPHA_INDEX_REPLACE_TASKBAR = 1; - private static final int NUM_ALPHA_CHANNELS = 2; - // Ratio of empty space, qsb should take up to appear visually centered. public static final float QSB_CENTER_FACTOR = .325f; @@ -52,10 +46,12 @@ public class Hotseat extends CellLayout implements Insettable { @Nullable private Consumer mOnVisibilityAggregatedCallback; - private final MultiValueAlpha mMultiValueAlpha; private final View mQsb; private final int mQsbHeight; + private final View mTaskbarView; + private final int mTaskbarViewHeight; + public Hotseat(Context context) { this(context, null); } @@ -66,12 +62,15 @@ public class Hotseat extends CellLayout implements Insettable { public Hotseat(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS, MultiValueAlpha.Mode.MAX); - mMultiValueAlpha.setUpdateVisibility(true); mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false); mQsbHeight = mQsb.getLayoutParams().height; addView(mQsb); + + mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false); + mTaskbarViewHeight = mTaskbarView.getLayoutParams().height; + // We want taskbar in the back so its background applies to Hotseat as well. + addView(mTaskbarView, 0); } /** @@ -193,6 +192,8 @@ public class Hotseat extends CellLayout implements Insettable { int width = getShortcutsAndWidgets().getMeasuredWidth(); mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY)); + mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY)); } @Override @@ -212,6 +213,13 @@ public class Hotseat extends CellLayout implements Insettable { - (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom); int top = bottom - mQsbHeight; mQsb.layout(left, top, right, bottom); + + int taskbarWidth = mTaskbarView.getMeasuredWidth(); + left = (r - l - taskbarWidth) / 2; + right = left + taskbarWidth; + bottom = b - t; + top = bottom - mTaskbarViewHeight; + mTaskbarView.layout(left, top, right, bottom); } /** @@ -221,12 +229,11 @@ public class Hotseat extends CellLayout implements Insettable { return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator); } - public MultiValueAlpha.AlphaProperty getStateAlpha() { - return mMultiValueAlpha.getProperty(ALPHA_INDEX_STATE); - } - - public MultiValueAlpha.AlphaProperty getReplaceTaskbarAlpha() { - return mMultiValueAlpha.getProperty(ALPHA_INDEX_REPLACE_TASKBAR); + /** + * Sets the alpha value of just our ShortcutAndWidgetContainer. + */ + public void setIconsAlpha(float alpha) { + getShortcutsAndWidgets().setAlpha(alpha); } /** @@ -235,4 +242,11 @@ public class Hotseat extends CellLayout implements Insettable { public View getQsb() { return mQsb; } + + /** + * Returns the Taskbar inside hotseat + */ + public View getTaskbarView() { + return mTaskbarView; + } } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index e9a34953d5..0f8c6b9172 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -185,12 +185,7 @@ public abstract class LauncherState implements BaseState { } public int getVisibleElements(Launcher launcher) { - DeviceProfile deviceProfile = launcher.getDeviceProfile(); - int flags = WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR; - if (!deviceProfile.isTaskbarPresent) { - flags |= HOTSEAT_ICONS; - } - return flags; + return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR; } /** diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 412754e211..cfcf0a0ced 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -54,7 +54,6 @@ import com.android.launcher3.graphics.SysUiScrim; import com.android.launcher3.graphics.WorkspaceDragScrim; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.util.DynamicResource; -import com.android.launcher3.util.MultiValueAlpha; import com.android.systemui.plugins.ResourceProvider; /** @@ -135,8 +134,8 @@ public class WorkspaceStateTransitionAnimation { } float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0; - propertySetter.setFloat(hotseat.getStateAlpha(), MultiValueAlpha.VALUE, - hotseatIconsAlpha, config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator)); + propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, + config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator)); float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0; propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(), workspacePageIndicatorAlpha, fadeInterpolator); diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 505c6cea42..71aa4ac445 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -18,6 +18,7 @@ package com.android.launcher3.views; import android.content.Context; import android.content.ContextWrapper; import android.graphics.Rect; +import android.view.LayoutInflater; import android.view.View.AccessibilityDelegate; import com.android.launcher3.DeviceProfile; @@ -66,6 +67,18 @@ public interface ActivityContext { default void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) { } + /** + * Returns a LayoutInflater that is cloned in this Context, so that Views inflated by it will + * have the same Context. (i.e. {@link #lookupContext(Context)} will find this ActivityContext.) + */ + default LayoutInflater getLayoutInflater() { + if (this instanceof Context) { + Context context = (Context) this; + return LayoutInflater.from(context).cloneInContext(context); + } + return null; + } + /** * The root view to support drag-and-drop and popup support. */ @@ -73,7 +86,10 @@ public interface ActivityContext { DeviceProfile getDeviceProfile(); - static T lookupContext(Context context) { + /** + * Returns the ActivityContext associated with the given Context. + */ + static T lookupContext(Context context) { if (context instanceof ActivityContext) { return (T) context; } else if (context instanceof ContextWrapper) {