Merge "Refactor grid migration" into main
diff --git a/quickstep/res/layout/taskbar_overflow_button.xml b/quickstep/res/layout/taskbar_overflow_view.xml
similarity index 86%
rename from quickstep/res/layout/taskbar_overflow_button.xml
rename to quickstep/res/layout/taskbar_overflow_view.xml
index 20104f2..7444e59 100644
--- a/quickstep/res/layout/taskbar_overflow_button.xml
+++ b/quickstep/res/layout/taskbar_overflow_view.xml
@@ -15,8 +15,7 @@
-->
<!-- Note: The actual size will match the taskbar icon sizes in TaskbarView#onLayout(). -->
-<com.android.launcher3.views.IconButtonView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/BaseIcon.Workspace.Taskbar"
+<com.android.launcher3.taskbar.TaskbarOverflowView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/taskbar_icon_min_touch_size"
android:layout_height="@dimen/taskbar_icon_min_touch_size"
android:backgroundTint="@android:color/transparent"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 5f35007..451ba55 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -360,6 +360,7 @@
<dimen name="taskbar_running_app_indicator_width">12dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">4dp</dimen>
<dimen name="taskbar_minimized_app_indicator_width">6dp</dimen>
+ <dimen name="taskbar_overflow_button_preview_stroke">2dp</dimen>
<!-- Transient taskbar -->
<dimen name="transient_taskbar_padding">12dp</dimen>
@@ -502,6 +503,7 @@
<dimen name="keyboard_quick_switch_recents_icon_size">20dp</dimen>
<dimen name="keyboard_quick_switch_desktop_icon_size">32dp</dimen>
<dimen name="keyboard_quick_switch_margin_top">56dp</dimen>
+ <dimen name="keyboard_quick_switch_margin_bottom">24dp</dimen>
<dimen name="keyboard_quick_switch_margin_ends">16dp</dimen>
<dimen name="keyboard_quick_switch_view_spacing">16dp</dimen>
<dimen name="keyboard_quick_switch_view_small_spacing">4dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index a80c11c..2902d55 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -20,6 +20,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.content.res.Resources;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -30,6 +31,7 @@
import androidx.annotation.Nullable;
import com.android.internal.jank.Cuj;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
@@ -115,6 +117,12 @@
BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
mKeyboardQuickSwitchView.getLayoutParams());
+ final Resources resources = mKeyboardQuickSwitchView.getResources();
+ final int marginHorizontal = resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_margin_ends);
+ final int marginBottom = resources.getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_margin_bottom);
+ lp.setMargins(marginHorizontal, 0, marginHorizontal, marginBottom);
lp.width = BaseDragLayer.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
mKeyboardQuickSwitchView.setLayoutParams(lp);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index f3741b2..6f1e96f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -297,6 +297,7 @@
BubbleStashController bubbleStashController = isTransientTaskbar
? new TransientBubbleStashController(dimensionsProvider, this)
: new PersistentBubbleStashController(dimensionsProvider);
+ bubbleStashController.setHotseatVerticalCenter(launcherDp.getHotseatVerticalCenter());
bubbleControllersOptional = Optional.of(new BubbleControllers(
new BubbleBarController(this, bubbleBarView),
new BubbleBarViewController(this, bubbleBarView, bubbleBarContainer),
@@ -362,8 +363,11 @@
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile launcherDp) {
applyDeviceProfile(launcherDp);
-
mControllers.taskbarOverlayController.updateLauncherDeviceProfile(launcherDp);
+ mControllers.bubbleControllers.ifPresent(bubbleControllers -> {
+ int hotseatVertCenter = launcherDp.getHotseatVerticalCenter();
+ bubbleControllers.bubbleStashController.setHotseatVerticalCenter(hotseatVertCenter);
+ });
AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
// Reapply fullscreen to take potential new screen size into account.
setTaskbarWindowFullscreen(mIsFullscreen);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index ade8f8c..a89bc3a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -265,7 +265,8 @@
!DisplayController.isPinnedTaskbar(activityContext) ||
!isTooltipEnabled ||
!shouldShowSearchEdu ||
- userHasSeenSearchEdu
+ userHasSeenSearchEdu ||
+ !controllers.taskbarStashController.isTaskbarVisibleAndNotStashing
) {
return
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 058dd07..4a6b6d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -145,6 +145,7 @@
// if bubble bar is visible or animating new bubble, add bar bounds to the touch region
if (isBubbleBarVisible || isAnimatingNewBubble) {
defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.bubbleBarBounds)
+ defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.flyoutBounds)
}
}
if (
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
new file mode 100644
index 0000000..fad5ca3
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarOverflowView.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Reorderable;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiTranslateDelegate;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * View used as overflow icon within task bar, when the list of recent/running apps overflows the
+ * available display bounds - if display is not wide enough to show all running apps in the taskbar,
+ * this icon is added to the taskbar as an entry point to open UI that surfaces all running apps.
+ * The icon contains icon representations of up to 4 more recent tasks in overflow, stacked on top
+ * each other in counter clockwise manner (icons of tasks partially overlapping with each other).
+ */
+public class TaskbarOverflowView extends FrameLayout implements Reorderable {
+ private final List<Task> mItems = new ArrayList<Task>();
+ private int mIconSize;
+ private int mPadding;
+ private Paint mItemBackgroundPaint;
+ private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
+ private float mScaleForReorderBounce = 1f;
+
+ public TaskbarOverflowView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public TaskbarOverflowView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Inflates the taskbar overflow button view.
+ * @param resId The resource to inflate the view from.
+ * @param group The parent view.
+ * @param iconSize The size of the overflow button icon.
+ * @param padding The internal padding of the overflow view.
+ * @return A taskbar overflow button.
+ */
+ public static TaskbarOverflowView inflateIcon(int resId, ViewGroup group, int iconSize,
+ int padding) {
+ LayoutInflater inflater = LayoutInflater.from(group.getContext());
+ TaskbarOverflowView icon = (TaskbarOverflowView) inflater.inflate(resId, group, false);
+
+ icon.mIconSize = iconSize;
+ icon.mPadding = padding;
+ return icon;
+ }
+
+ private void init() {
+ mItemBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mItemBackgroundPaint.setColor(getContext().getColor(R.color.taskbar_background));
+
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ boolean isRtlLayout = Utilities.isRtl(getResources());
+ float radius = mIconSize / 2.0f - mPadding;
+ float itemPreviewStrokeWidth =
+ getResources().getDimension(R.dimen.taskbar_overflow_button_preview_stroke);
+
+ int itemsToShow = Math.min(mItems.size(), 4);
+ for (int i = itemsToShow - 1; i >= 0; --i) {
+ Drawable icon = mItems.get(mItems.size() - i - 1).icon;
+ if (icon == null) {
+ continue;
+ }
+
+ // Set the item icon size so two items fit within the overflow icon with stroke width
+ // included, and overlap of 4 stroke width sizes between base item preview items.
+ // 2 * strokeWidth + 2 * itemIconSize - 4 * strokeWidth = iconSize = 2 * radius.
+ float itemIconSize = radius + itemPreviewStrokeWidth;
+ // Offset item icon from center so item icon stroke edge mateches the parent icon edge.
+ float itemCenterOffset = radius - itemIconSize / 2 - itemPreviewStrokeWidth;
+
+ float itemCenterX = getItemXOffset(itemCenterOffset, isRtlLayout, i, itemsToShow);
+ float itemCenterY = getItemYOffset(itemCenterOffset, i, itemsToShow);
+
+ Drawable iconCopy = icon.getConstantState().newDrawable().mutate();
+ iconCopy.setBounds(0, 0, (int) itemIconSize, (int) itemIconSize);
+
+ canvas.save();
+ float itemIconRadius = itemIconSize / 2;
+ canvas.translate(
+ mPadding + itemCenterX + radius - itemIconRadius,
+ mPadding + itemCenterY + radius - itemIconRadius);
+ canvas.drawCircle(itemIconRadius, itemIconRadius,
+ itemIconRadius + itemPreviewStrokeWidth, mItemBackgroundPaint);
+ iconCopy.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Clears the list of tasks tracked by the view.
+ */
+ public void clearItems() {
+ mItems.clear();
+ invalidate();
+ }
+
+ /**
+ * Update the view to represent a new list of recent tasks.
+ * @param items Items to be shown in the view.
+ */
+ public void setItems(List<Task> items) {
+ mItems.clear();
+ mItems.addAll(items);
+ invalidate();
+ }
+
+ /**
+ * Called when a task is updated. If the task is contained within the view, it's cached value
+ * gets updated. If the task is shown within the icon, invalidates the view, so the task icon
+ * gets updated.
+ * @param task The updated task.
+ */
+ public void updateTaskIsShown(Task task) {
+ for (int i = 0; i < mItems.size(); ++i) {
+ if (mItems.get(i).key.id == task.key.id) {
+ mItems.set(i, task);
+ if (i >= mItems.size() - 4) {
+ invalidate();
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ public MultiTranslateDelegate getTranslateDelegate() {
+ return mTranslateDelegate;
+ }
+
+ @Override
+ public float getReorderBounceScale() {
+ return mScaleForReorderBounce;
+ }
+
+ @Override
+ public void setReorderBounceScale(float scale) {
+ mScaleForReorderBounce = scale;
+ super.setScaleX(scale);
+ super.setScaleY(scale);
+ }
+
+ private float getItemXOffset(float baseOffset, boolean isRtl, int itemIndex, int itemCount) {
+ // Item with index 1 is on the left in all cases.
+ if (itemIndex == 1) {
+ return (isRtl ? 1 : -1) * baseOffset;
+ }
+
+ // First item is centered if total number of items shown is 3, on the right otherwise.
+ if (itemIndex == 0) {
+ if (itemCount == 3) {
+ return 0;
+ }
+ return (isRtl ? -1 : 1) * baseOffset;
+ }
+
+ // Last item is on the right when there are more than 2 items (case which is already handled
+ // as `itemIndex == 1`).
+ if (itemIndex == itemCount - 1) {
+ return (isRtl ? -1 : 1) * baseOffset;
+ }
+
+ return (isRtl ? 1 : -1) * baseOffset;
+ }
+
+ private float getItemYOffset(float baseOffset, int itemIndex, int itemCount) {
+ // If icon contains two items, they are both centered vertically.
+ if (itemCount == 2) {
+ return 0;
+ }
+ // First half of items is on top, later half is on bottom.
+ return (itemIndex + 1 <= itemCount / 2 ? -1 : 1) * baseOffset;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index fcb583a..6cc03ab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -64,12 +64,12 @@
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.IconButtonView;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@@ -106,7 +106,7 @@
@Nullable private TaskbarDividerContainer mTaskbarDividerContainer;
// Only non-null when device supports having a Taskbar Overflow button.
- @Nullable private IconButtonView mTaskbarOverflowView;
+ @Nullable private TaskbarOverflowView mTaskbarOverflowView;
/**
* Whether the divider is between Hotseat icons and Recents,
@@ -122,6 +122,8 @@
private final int mMaxNumIcons;
+ private final int mAllAppsButtonTranslationOffset;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -141,8 +143,6 @@
mActivityContext = ActivityContext.lookupContext(context);
mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
Resources resources = getResources();
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
- && !mActivityContext.isPhoneMode();
mIsRtl = Utilities.isRtl(resources);
mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
@@ -173,18 +173,19 @@
setWillNotDraw(false);
mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
+ mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
+ mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(isTransientTaskbar()));
if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
mTaskbarDividerContainer = new TaskbarDividerContainer(context);
}
if (Flags.taskbarOverflow()) {
- mTaskbarOverflowView = (IconButtonView) LayoutInflater.from(context)
- .inflate(R.layout.taskbar_overflow_button, this, false);
- mTaskbarOverflowView.setIconDrawable(
- resources.getDrawable(R.drawable.taskbar_overflow_icon));
- mTaskbarOverflowView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ mTaskbarOverflowView = TaskbarOverflowView.inflateIcon(
+ R.layout.taskbar_overflow_view, this,
+ mIconTouchSize, mItemPadding);
}
+
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
@@ -222,9 +223,7 @@
enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
availableWidth -= iconSize - (int) getResources().getDimension(
mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
- forceTransientTaskbarSize || (
- DisplayController.isTransientTaskbar(mActivityContext)
- && !mActivityContext.isPhoneMode())));
+ forceTransientTaskbarSize || isTransientTaskbar()));
++additionalIcons;
return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
@@ -449,24 +448,47 @@
int nonTaskIconsToBeAdded = 1;
boolean supportsOverflow = Flags.taskbarOverflow();
+ int overflowSize = 0;
if (supportsOverflow) {
int numberOfSupportedRecents = 0;
for (GroupTask task : recentTasks) {
// TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
- if (!task.hasMultipleTasks()) {
+ if (!task.supportsMultipleTasks()) {
++numberOfSupportedRecents;
}
}
- if (nextViewIndex + numberOfSupportedRecents + nonTaskIconsToBeAdded > mMaxNumIcons
- && mTaskbarOverflowView != null) {
+
+ overflowSize =
+ nextViewIndex + numberOfSupportedRecents + nonTaskIconsToBeAdded - mMaxNumIcons;
+ if (overflowSize > 0 && mTaskbarOverflowView != null) {
addView(mTaskbarOverflowView, nextViewIndex++);
+ } else if (mTaskbarOverflowView != null) {
+ mTaskbarOverflowView.clearItems();
}
}
+ List<Task> overflownTasks = null;
+ // An extra item needs to be added to overflow button to account for the space taken up by
+ // the overflow button.
+ final int itemsToAddToOverflow = overflowSize > 0 ? overflowSize + 1 : 0;
+ if (overflowSize > 0) {
+ overflownTasks = new ArrayList<Task>(itemsToAddToOverflow);
+ }
+
// Add Recent/Running icons.
for (GroupTask task : recentTasks) {
- if (supportsOverflow && nextViewIndex + nonTaskIconsToBeAdded >= mMaxNumIcons) {
- break;
+ if (mTaskbarOverflowView != null && overflownTasks != null
+ && overflownTasks.size() < itemsToAddToOverflow) {
+ // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
+ if (task.supportsMultipleTasks()) {
+ continue;
+ }
+
+ overflownTasks.add(task.task1);
+ if (overflownTasks.size() == itemsToAddToOverflow) {
+ mTaskbarOverflowView.setItems(overflownTasks);
+ }
+ continue;
}
// Replace any Recent views with the appropriate type if it's not already that type.
@@ -668,6 +690,15 @@
mIconLayoutBounds.right = iconEnd;
mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
+
+ // With rtl layout, the all apps button will be translated by `allAppsButtonOffset` after
+ // layout completion (by `TaskbarViewController`). Offset the icon end by the same amount
+ // when laying out icons, so the taskbar content remains centered after all apps button
+ // translation.
+ if (layoutRtl) {
+ iconEnd += mAllAppsButtonTranslationOffset;
+ }
+
int count = getChildCount();
for (int i = count; i > 0; i--) {
View child = getChildAt(i - 1);
@@ -699,6 +730,15 @@
mIconLayoutBounds.left = iconEnd;
+ // Adjust the icon layout bounds by the amount by which all apps button will be translated
+ // post layout to maintain margin between all apps button and the edge of the transient
+ // taskbar background. Done for ltr layout only - for rtl layout, the offset needs to be
+ // adjusted on the right, which is done by offsetting `iconEnd` after setting
+ // `mIconLayoutBounds.right`.
+ if (!layoutRtl) {
+ mIconLayoutBounds.left += mAllAppsButtonTranslationOffset;
+ }
+
if (mIconLayoutBounds.right - mIconLayoutBounds.left < mTransientTaskbarMinWidth) {
int center = mIconLayoutBounds.centerX();
int distanceFromCenter = (int) mTransientTaskbarMinWidth / 2;
@@ -741,12 +781,13 @@
/**
* Returns the space used by the icons
*/
- public int getIconLayoutWidth() {
+ private int getIconLayoutWidth() {
int countExcludingQsb = getChildCount();
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
if (deviceProfile.isQsbInline) {
countExcludingQsb--;
}
+
int iconLayoutBoundsWidth =
countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
@@ -755,17 +796,28 @@
// All Apps icon, divider icon, and first app icon in taskbar
iconLayoutBoundsWidth -= mItemMarginLeftRight * 4;
}
+
+ // The all apps button container gets offset horizontally, reducing the overall taskbar
+ // view size.
+ iconLayoutBoundsWidth -= mAllAppsButtonTranslationOffset;
+
return iconLayoutBoundsWidth;
}
/**
- * Returns the app icons currently shown in the taskbar.
+ * Returns the app icons currently shown in the taskbar. The returned list does not include qsb,
+ * but it includes all apps button and icon divider views.
*/
public View[] getIconViews() {
final int count = getChildCount();
- View[] icons = new View[count];
+ if (count == 0) {
+ return new View[0];
+ }
+ View[] icons = new View[count - (mActivityContext.getDeviceProfile().isQsbInline ? 1 : 0)];
+ int insertionPoint = 0;
for (int i = 0; i < count; i++) {
- icons[i] = getChildAt(i);
+ if (getChildAt(i) == mQsb) continue;
+ icons[insertionPoint++] = getChildAt(i);
}
return icons;
}
@@ -789,7 +841,7 @@
* Returns the taskbar overflow view in the taskbar.
*/
@Nullable
- public IconButtonView getTaskbarOverflowView() {
+ public TaskbarOverflowView getTaskbarOverflowView() {
return mTaskbarOverflowView;
}
@@ -847,12 +899,25 @@
// Ignore, we just implement Insettable to draw behind system insets.
}
+ private boolean isTransientTaskbar() {
+ return DisplayController.isTransientTaskbar(mActivityContext)
+ && !mActivityContext.isPhoneMode();
+ }
+
public boolean areIconsVisible() {
// Consider the overall visibility
return getVisibility() == VISIBLE;
}
/**
+ * @return The all apps button horizontal offset used to calculate the taskbar contents width
+ * during layout.
+ */
+ public int getAllAppsButtonTranslationXOffsetUsedForLayout() {
+ return mAllAppsButtonTranslationOffset;
+ }
+
+ /**
* Maps {@code op} over all the child views.
*/
public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 253d025..6f2d459 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -359,10 +359,6 @@
return mTaskbarView.getIconLayoutBounds();
}
- public int getIconLayoutWidth() {
- return mTaskbarView.getIconLayoutWidth();
- }
-
public View[] getIconViews() {
return mTaskbarView.getIconViews();
}
@@ -442,6 +438,19 @@
float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset,
persistentTaskbarAllAppsOffset);
+ // Task icons are laid out so the taskbar content is centered. The taskbar width (used for
+ // centering taskbar icons) depends on the all apps button X translation, and is different
+ // for persistent and transient taskbar. If the offset used for current taskbar layout is
+ // different than the offset used in final taskbar state, the icons may jump when the
+ // animation completes, and the taskbar is replaced. Adjust item transform to account for
+ // this mismatch.
+ float sizeDiffTranslationRange =
+ mapRange(scale,
+ (mTaskbarView.getAllAppsButtonTranslationXOffsetUsedForLayout()
+ - transientTaskbarAllAppsOffset) / 2,
+ (mTaskbarView.getAllAppsButtonTranslationXOffsetUsedForLayout()
+ - persistentTaskbarAllAppsOffset) / 2);
+
// no x translation required when all apps button is the only icon in taskbar.
if (iconViews.length <= 1) {
allAppIconTranslateRange = 0f;
@@ -449,6 +458,7 @@
if (mIsRtl) {
allAppIconTranslateRange *= -1;
+ sizeDiffTranslationRange *= -1;
}
if (mActivity.isThreeButtonNav()) {
@@ -457,25 +467,18 @@
return;
}
- float taskbarCenterX =
- mTaskbarView.getLeft() + (mTaskbarView.getRight() - mTaskbarView.getLeft()) / 2.0f;
-
float finalMarginScale = mapRange(scale, 0f, mTransientIconSize - mPersistentIconSize);
- float halfIconCount = iconViews.length / 2.0f;
+ // The index of the "middle" icon which will be used as a index from which the icon margins
+ // will be scaled. If number of icons is even, using the middle point between indices of two
+ // central icons.
+ float middleIndex = (iconViews.length - 1) / 2.0f;
for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
View iconView = iconViews[iconIndex];
MultiTranslateDelegate translateDelegate =
((Reorderable) iconView).getTranslateDelegate();
- float iconCenterX =
- iconView.getLeft() + (iconView.getRight() - iconView.getLeft()) / 2.0f;
- if (iconCenterX <= taskbarCenterX) {
- translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
- finalMarginScale * (halfIconCount - iconIndex));
- } else {
- translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
- -finalMarginScale * (iconIndex - halfIconCount));
- }
+ translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
+ finalMarginScale * (middleIndex - iconIndex) + sizeDiffTranslationRange);
if (iconView.equals(mTaskbarView.getAllAppsButtonContainer())) {
mTaskbarView.getAllAppsButtonContainer().setTranslationXForTaskbarAllAppsIcon(
@@ -488,18 +491,14 @@
* Calculates visual taskbar view width.
*/
public float getCurrentVisualTaskbarWidth() {
- if (mTaskbarView.getIconViews().length == 0) {
+ View[] iconViews = mTaskbarView.getIconViews();
+ if (iconViews.length == 0) {
return 0;
}
- View[] iconViews = mTaskbarView.getIconViews();
+ float left = iconViews[0].getX();
- int leftIndex = mActivity.getDeviceProfile().isQsbInline && !mIsRtl ? 1 : 0;
- int rightIndex = mActivity.getDeviceProfile().isQsbInline && mIsRtl
- ? iconViews.length - 2
- : iconViews.length - 1;
-
- float left = iconViews[leftIndex].getX();
+ int rightIndex = iconViews.length - 1;
float right = iconViews[rightIndex].getRight() + iconViews[rightIndex].getTranslationX();
return right - left + (2 * mTaskbarLeftRightMargin);
@@ -1090,6 +1089,8 @@
if (groupTask.containsTask(task.key.id)) {
mTaskbarView.applyGroupTaskToBubbleTextView(btv, groupTask);
}
+ } else if (view instanceof TaskbarOverflowView overflowButton) {
+ overflowButton.updateTaskIsShown(task);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 76d3606..717ee95 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -45,6 +45,7 @@
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
+import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
@@ -146,7 +147,7 @@
mTaskbarStashController = controllers.taskbarStashController;
mTaskbarInsetsController = controllers.taskbarInsetsController;
mBubbleBarFlyoutController = new BubbleBarFlyoutController(
- mBubbleBarContainer, createFlyoutPositioner(), createFlyoutTopBoundaryListener());
+ mBubbleBarContainer, createFlyoutPositioner(), createFlyoutCallbacks());
mBubbleBarViewAnimator = new BubbleBarViewAnimator(
mBarView, mBubbleStashController, mBubbleBarFlyoutController,
mBubbleBarController::showExpandedView);
@@ -175,7 +176,9 @@
@Override
public void onBubbleBarTouched() {
- BubbleBarViewController.this.onBubbleBarTouched();
+ if (isAnimatingNewBubble()) {
+ interruptAnimationForTouch();
+ }
}
@Override
@@ -273,8 +276,8 @@
};
}
- private BubbleBarFlyoutController.TopBoundaryListener createFlyoutTopBoundaryListener() {
- return new BubbleBarFlyoutController.TopBoundaryListener() {
+ private FlyoutCallbacks createFlyoutCallbacks() {
+ return new FlyoutCallbacks() {
@Override
public void extendTopBoundary(int space) {
int defaultSize = mActivity.getDefaultTaskbarWindowSize();
@@ -285,6 +288,12 @@
public void resetTopBoundary() {
mActivity.setTaskbarWindowSize(mActivity.getDefaultTaskbarWindowSize());
}
+
+ @Override
+ public void flyoutClicked() {
+ interruptAnimationForTouch();
+ expandBubbleBar();
+ }
};
}
@@ -304,12 +313,10 @@
}
}
- private void onBubbleBarTouched() {
- if (isAnimatingNewBubble()) {
- mBubbleBarViewAnimator.onBubbleBarTouchedWhileAnimating();
- mBubbleStashController.onNewBubbleAnimationInterrupted(false,
- mBarView.getTranslationY());
- }
+ /** Interrupts the running animation for a touch event on the bubble bar or flyout. */
+ private void interruptAnimationForTouch() {
+ mBubbleBarViewAnimator.interruptForTouch();
+ mBubbleStashController.onNewBubbleAnimationInterrupted(false, mBarView.getTranslationY());
}
private void expandBubbleBar() {
@@ -463,6 +470,12 @@
return mBarView.getBubbleBarBounds();
}
+ /** Returns the bounds of the flyout view if it exists, or {@code null} otherwise. */
+ @Nullable
+ public Rect getFlyoutBounds() {
+ return mBubbleBarFlyoutController.getFlyoutBounds();
+ }
+
/** Checks that bubble bar is visible and that the motion event is within bounds. */
public boolean isEventOverBubbleBar(MotionEvent event) {
if (!isBubbleBarVisible()) return false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 8a52ca9..78e5dbd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -471,8 +471,8 @@
bubbleStashController.updateTaskbarTouchRegion()
}
- /** Handles touching the animating bubble bar. */
- fun onBubbleBarTouchedWhileAnimating() {
+ /** Interrupts the animation due to touching the bubble bar or flyout. */
+ fun interruptForTouch() {
PhysicsAnimator.getInstance(bubbleBarView).cancelIfRunning()
bubbleStashController.getStashedHandlePhysicsAnimator().cancelIfRunning()
cancelFlyout()
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
index d6400bb..fdbbbb0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -16,13 +16,13 @@
package com.android.launcher3.taskbar.bubbles.flyout
+import android.graphics.Rect
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.animation.ValueAnimator
import com.android.launcher3.R
import com.android.systemui.util.addListener
-import com.android.systemui.util.doOnEnd
/** Creates and manages the visibility of the [BubbleBarFlyoutView]. */
class BubbleBarFlyoutController
@@ -30,7 +30,7 @@
constructor(
private val container: FrameLayout,
private val positioner: BubbleBarFlyoutPositioner,
- private val topBoundaryListener: TopBoundaryListener,
+ private val callbacks: FlyoutCallbacks,
private val flyoutScheduler: FlyoutScheduler = HandlerScheduler(container),
) {
@@ -47,6 +47,15 @@
FADE,
}
+ /** The bounds of the flyout. */
+ val flyoutBounds: Rect?
+ get() {
+ val flyout = this.flyout ?: return null
+ val rect = Rect(flyout.bounds)
+ rect.offset(0, flyout.translationY.toInt())
+ return rect
+ }
+
fun setUpAndShowFlyout(message: BubbleBarFlyoutMessage, onEnd: () -> Unit) {
flyout?.let(container::removeView)
val flyout = BubbleBarFlyoutView(container.context, positioner, flyoutScheduler)
@@ -72,9 +81,12 @@
val flyoutTop = flyout.top + flyout.translationY
// If the top position of the flyout is negative, then it's bleeding over the
// top boundary of its parent view
- if (flyoutTop < 0) topBoundaryListener.extendTopBoundary(space = -flyoutTop.toInt())
+ if (flyoutTop < 0) callbacks.extendTopBoundary(space = -flyoutTop.toInt())
},
- onEnd = { onEnd() },
+ onEnd = {
+ onEnd()
+ flyout.setOnClickListener { callbacks.flyoutClicked() }
+ },
)
flyout.showFromCollapsed(message) { animator.start() }
this.flyout = flyout
@@ -100,21 +112,15 @@
flyout.updateExpansionProgress(animator.animatedValue as Float)
}
}
- animator.doOnEnd {
- container.removeView(flyout)
- this@BubbleBarFlyoutController.flyout = null
- topBoundaryListener.resetTopBoundary()
- endAction()
- }
+ animator.addListener(
+ onStart = { flyout.setOnClickListener(null) },
+ onEnd = {
+ container.removeView(flyout)
+ this@BubbleBarFlyoutController.flyout = null
+ callbacks.resetTopBoundary()
+ endAction()
+ },
+ )
animator.start()
}
-
- /** Notifies when the top boundary of the flyout view changes. */
- interface TopBoundaryListener {
- /** Requests to extend the top boundary of the parent to fully include the flyout. */
- fun extendTopBoundary(space: Int)
-
- /** Resets the top boundary of the parent. */
- fun resetTopBoundary()
- }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 6903c87..bb8a392 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -24,6 +24,7 @@
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
+import android.graphics.Rect
import android.graphics.RectF
import android.view.LayoutInflater
import android.view.View
@@ -138,6 +139,9 @@
*/
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
+ /** The bounds of the flyout relative to the parent view. */
+ val bounds = Rect()
+
init {
LayoutInflater.from(context).inflate(R.layout.bubblebar_flyout, this, true)
id = R.id.bubble_bar_flyout_view
@@ -174,6 +178,14 @@
applyConfigurationColors(resources.configuration)
}
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ bounds.left = left
+ bounds.top = top
+ bounds.right = right
+ bounds.bottom = bottom
+ }
+
/** Sets the data for the flyout and starts playing the expand animation. */
fun showFromCollapsed(flyoutMessage: BubbleBarFlyoutMessage, expandAnimation: () -> Unit) {
icon.alpha = 0f
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutCallbacks.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutCallbacks.kt
new file mode 100644
index 0000000..e2f010a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutCallbacks.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+/** Callbacks that the flyout uses to notify of events. */
+interface FlyoutCallbacks {
+ /** Requests to extend the top boundary of the parent to fully include the flyout. */
+ fun extendTopBoundary(space: Int)
+
+ /** Resets the top boundary of the parent. */
+ fun resetTopBoundary()
+
+ /** The flyout was clicked. */
+ fun flyoutClicked()
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index a78890b..831faa1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -42,12 +42,6 @@
/** Provides taskbar height in pixels. */
fun getTaskbarHeight(): Int
-
- /** Provides hotseat bottom space in pixels. */
- fun getHotseatBottomSpace(): Int
-
- /** Provides hotseat height in pixels. */
- fun getHotseatHeight(): Int
}
/** Execute passed action only after controllers are initiated. */
@@ -94,7 +88,7 @@
taskbarInsetsController: TaskbarInsetsController,
bubbleBarViewController: BubbleBarViewController,
bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
- controllersAfterInitAction: ControllersAfterInitAction
+ controllersAfterInitAction: ControllersAfterInitAction,
)
/** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
@@ -127,6 +121,9 @@
/** Set a bubble bar location */
fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation)
+ /** Set the hotseat vertical center that bubble bar will align with. */
+ fun setHotseatVerticalCenter(hotseatVerticalCenter: Int)
+
/**
* Stashes the bubble bar (transform to the handle view), or just shrink width of the expanded
* bubble bar based on the controller implementation.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
index a55763b..886b9f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
@@ -27,13 +27,9 @@
class DeviceProfileDimensionsProviderAdapter(
private val taskbarActivityContext: TaskbarActivityContext
) : TaskbarHotseatDimensionsProvider {
- override fun getTaskbarBottomSpace(): Int = deviceProfile().taskbarBottomMargin
+ override fun getTaskbarBottomSpace(): Int = taskbarDp().taskbarBottomMargin
- override fun getTaskbarHeight(): Int = deviceProfile().taskbarHeight
+ override fun getTaskbarHeight(): Int = taskbarDp().taskbarHeight
- override fun getHotseatBottomSpace(): Int = deviceProfile().hotseatBarBottomSpacePx
-
- override fun getHotseatHeight(): Int = deviceProfile().hotseatCellHeightPx
-
- private fun deviceProfile(): DeviceProfile = taskbarActivityContext.deviceProfile
+ private fun taskbarDp(): DeviceProfile = taskbarActivityContext.deviceProfile
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index 722dfe7..c117ad4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -36,7 +36,7 @@
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
class PersistentBubbleStashController(
- private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
+ private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider
) : BubbleStashController {
private lateinit var taskbarInsetsController: TaskbarInsetsController
@@ -45,6 +45,7 @@
private lateinit var bubbleBarAlphaAnimator: MultiPropertyFactory<View>.MultiProperty
private lateinit var bubbleBarScaleAnimator: AnimatedFloat
private lateinit var controllersAfterInitAction: ControllersAfterInitAction
+ private var hotseatVerticalCenter: Int = 0
override var launcherState: BubbleLauncherState = BubbleLauncherState.IN_APP
set(state) {
@@ -92,17 +93,15 @@
override val bubbleBarTranslationYForHotseat: Float
get() {
- val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
- val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
- val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
- return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
+ val bubbleBarHeight = bubbleBarViewController.bubbleBarCollapsedHeight
+ return -hotseatVerticalCenter + bubbleBarHeight / 2
}
override fun init(
taskbarInsetsController: TaskbarInsetsController,
bubbleBarViewController: BubbleBarViewController,
bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
- controllersAfterInitAction: ControllersAfterInitAction
+ controllersAfterInitAction: ControllersAfterInitAction,
) {
this.taskbarInsetsController = taskbarInsetsController
this.bubbleBarViewController = bubbleBarViewController
@@ -119,13 +118,17 @@
animatorSet.playTogether(
bubbleBarScaleAnimator.animateToValue(1f),
bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
- bubbleBarAlphaAnimator.animateToValue(1f)
+ bubbleBarAlphaAnimator.animateToValue(1f),
)
}
updateTouchRegionOnAnimationEnd(animatorSet)
animatorSet.setDuration(BAR_STASH_DURATION).start()
}
+ override fun setHotseatVerticalCenter(hotseatVerticalCenter: Int) {
+ this.hotseatVerticalCenter = hotseatVerticalCenter
+ }
+
override fun showBubbleBarImmediate() = showBubbleBarImmediate(bubbleBarTranslationY)
override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 9e7d1c4..fbeecaa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -78,6 +78,7 @@
context.resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
private var animator: AnimatorSet? = null
+ private var hotseatVerticalCenter: Int = 0
override var isStashed: Boolean = false
@VisibleForTesting set
@@ -118,10 +119,8 @@
override val bubbleBarTranslationYForHotseat: Float
get() {
- val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
- val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
- val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
- return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
+ val bubbleBarHeight = bubbleBarViewController.bubbleBarCollapsedHeight
+ return -hotseatVerticalCenter + bubbleBarHeight / 2
}
override val bubbleBarTranslationYForTaskbar: Float =
@@ -176,6 +175,10 @@
.start()
}
+ override fun setHotseatVerticalCenter(hotseatVerticalCenter: Int) {
+ this.hotseatVerticalCenter = hotseatVerticalCenter
+ }
+
override fun showBubbleBarImmediate() {
showBubbleBarImmediate(bubbleBarTranslationY)
}
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index 2f0a6df..db02f55 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -16,10 +16,13 @@
package com.android.quickstep.util
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.graphics.Matrix
import android.graphics.Path
import android.graphics.RectF
+import android.util.Log
import android.view.View
import android.view.animation.PathInterpolator
import androidx.core.graphics.transform
@@ -41,6 +44,8 @@
import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.quickstep.views.RecentsView
+const val TAG = "ScalingWorkspaceRevealAnim"
+
/**
* Creates an animation where the workspace and hotseat fade in while revealing from the center of
* the screen outwards radially. This is used in conjunction with the swipe up to home animation.
@@ -197,6 +202,19 @@
workspace.setLayerType(View.LAYER_TYPE_HARDWARE, null)
hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null)
animation.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationCancel(animation: Animator) {
+ super.onAnimationCancel(animation)
+ Log.d(TAG, "onAnimationCancel")
+ }
+
+ override fun onAnimationPause(animation: Animator) {
+ super.onAnimationPause(animation)
+ Log.d(TAG, "onAnimationPause")
+ }
+ }
+ )
+ animation.addListener(
AnimatorListeners.forEndCallback(
Runnable {
workspace.setLayerType(View.LAYER_TYPE_NONE, null)
@@ -207,6 +225,8 @@
Animations.setOngoingAnimation(workspace, animation = null)
Animations.setOngoingAnimation(hotseat, animation = null)
}
+
+ Log.d(TAG, "alpha of workspace at the end of animation: ${workspace.alpha}")
}
)
)
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
index 0a97793..32e0e13 100644
--- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -30,6 +30,7 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
+import android.util.Log;
import android.view.View;
import com.android.app.animation.Interpolators;
@@ -51,6 +52,8 @@
*/
public class WorkspaceRevealAnim {
+ private static final String TAG = "WorkspaceRevealAnim";
+
// Should be used for animations running alongside this WorkspaceRevealAnim.
public static final int DURATION_MS = 350;
private static final FloatProperty<Workspace<?>> WORKSPACE_SCALE_PROPERTY =
@@ -97,6 +100,19 @@
mAnimators.setDuration(DURATION_MS);
mAnimators.setInterpolator(Interpolators.DECELERATED_EASE);
+ mAnimators.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ Log.d(TAG, "onAnimationCancel");
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ Log.d(TAG, "onAnimationEnd: workspace alpha = " + workspace.getAlpha());
+ }
+ });
}
private <T extends View> void addRevealAnimatorsForView(T v, FloatProperty<T> scaleProperty) {
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index b37048a..b0d01d3 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -40,6 +40,7 @@
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutMessage
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner
+import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks
import com.android.launcher3.taskbar.bubbles.flyout.FlyoutScheduler
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.shared.animation.PhysicsAnimator
@@ -179,9 +180,7 @@
// verify the hide bubble animation is pending
assertThat(animatorScheduler.delayedBlock).isNotNull()
- InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animator.onBubbleBarTouchedWhileAnimating()
- }
+ InstrumentationRegistry.getInstrumentation().runOnMainSync { animator.interruptForTouch() }
waitForFlyoutToHide()
@@ -992,18 +991,20 @@
override val collapsedElevation = 1f
override val distanceToRevealTriangle = 10f
}
- val topBoundaryListener =
- object : BubbleBarFlyoutController.TopBoundaryListener {
+ val flyoutCallbacks =
+ object : FlyoutCallbacks {
override fun extendTopBoundary(space: Int) {}
override fun resetTopBoundary() {}
+
+ override fun flyoutClicked() {}
}
val flyoutScheduler = FlyoutScheduler { block -> block.invoke() }
flyoutController =
BubbleBarFlyoutController(
flyoutContainer,
flyoutPositioner,
- topBoundaryListener,
+ flyoutCallbacks,
flyoutScheduler,
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index 527bdaa..0eea741 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -44,7 +44,7 @@
private lateinit var flyoutController: BubbleBarFlyoutController
private lateinit var flyoutContainer: FrameLayout
- private lateinit var topBoundaryListener: FakeTopBoundaryListener
+ private lateinit var flyoutCallbacks: FakeFlyoutCallbacks
private val context = ApplicationProvider.getApplicationContext<Context>()
private val flyoutMessage = BubbleBarFlyoutMessage(icon = null, "sender name", "message")
private var onLeft = true
@@ -67,15 +67,10 @@
override val collapsedElevation = 1f
override val distanceToRevealTriangle = 50f
}
- topBoundaryListener = FakeTopBoundaryListener()
+ flyoutCallbacks = FakeFlyoutCallbacks()
val flyoutScheduler = FlyoutScheduler { block -> block.invoke() }
flyoutController =
- BubbleBarFlyoutController(
- flyoutContainer,
- positioner,
- topBoundaryListener,
- flyoutScheduler,
- )
+ BubbleBarFlyoutController(flyoutContainer, positioner, flyoutCallbacks, flyoutScheduler)
}
@Test
@@ -140,7 +135,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(300)
}
- assertThat(topBoundaryListener.topBoundaryExtendedSpace).isEqualTo(50)
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
}
@Test
@@ -153,7 +148,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animatorTestRule.advanceTimeBy(300)
}
- assertThat(topBoundaryListener.topBoundaryExtendedSpace).isEqualTo(0)
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(0)
}
@Test
@@ -164,7 +159,7 @@
flyoutController.collapseFlyout {}
animatorTestRule.advanceTimeBy(300)
}
- assertThat(topBoundaryListener.topBoundaryReset).isTrue()
+ assertThat(flyoutCallbacks.topBoundaryReset).isTrue()
}
@Test
@@ -178,13 +173,27 @@
animatorTestRule.advanceTimeBy(300)
assertThat(flyoutView.alpha).isEqualTo(0f)
}
- assertThat(topBoundaryListener.topBoundaryReset).isTrue()
+ assertThat(flyoutCallbacks.topBoundaryReset).isTrue()
}
- class FakeTopBoundaryListener : BubbleBarFlyoutController.TopBoundaryListener {
+ @Test
+ fun clickFlyout_notifiesCallback() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyoutView = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ assertThat(flyoutView.alpha).isEqualTo(1f)
+ animatorTestRule.advanceTimeBy(300)
+ flyoutView.performClick()
+ }
+ assertThat(flyoutCallbacks.flyoutClicked).isTrue()
+ }
+
+ class FakeFlyoutCallbacks : FlyoutCallbacks {
var topBoundaryExtendedSpace = 0
var topBoundaryReset = false
+ var flyoutClicked = false
override fun extendTopBoundary(space: Int) {
topBoundaryExtendedSpace = space
@@ -193,5 +202,9 @@
override fun resetTopBoundary() {
topBoundaryReset = true
}
+
+ override fun flyoutClicked() {
+ flyoutClicked = true
+ }
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
index 5dc78a9..00b42bc 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -48,6 +48,7 @@
companion object {
const val BUBBLE_BAR_HEIGHT = 100f
+ const val HOTSEAT_VERTICAL_CENTER = 95
const val HOTSEAT_TRANSLATION_Y = -45f
const val TASK_BAR_TRANSLATION_Y = -5f
}
@@ -74,11 +75,12 @@
PersistentBubbleStashController(DefaultDimensionsProvider())
setUpBubbleBarView()
setUpBubbleBarController()
+ persistentTaskBarStashController.setHotseatVerticalCenter(HOTSEAT_VERTICAL_CENTER)
persistentTaskBarStashController.init(
taskbarInsetsController,
bubbleBarViewController,
null,
- ImmediateAction()
+ ImmediateAction(),
)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt
index 0f8a2c3..96c2f45 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt
@@ -23,21 +23,13 @@
class DefaultDimensionsProvider(
private val taskBarBottomSpace: Int = TASKBAR_BOTTOM_SPACE,
private val taskBarHeight: Int = TASKBAR_HEIGHT,
- private val hotseatBottomSpace: Int = HOTSEAT_BOTTOM_SPACE,
- private val hotseatHeight: Int = HOTSEAT_HEIGHT
) : BubbleStashController.TaskbarHotseatDimensionsProvider {
override fun getTaskbarBottomSpace(): Int = taskBarBottomSpace
override fun getTaskbarHeight(): Int = taskBarHeight
- override fun getHotseatBottomSpace(): Int = hotseatBottomSpace
-
- override fun getHotseatHeight(): Int = hotseatHeight
-
companion object {
const val TASKBAR_BOTTOM_SPACE = 0
const val TASKBAR_HEIGHT = 110
- const val HOTSEAT_BOTTOM_SPACE = 20
- const val HOTSEAT_HEIGHT = 150
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index 8b277e7..64416dd 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -60,6 +60,7 @@
companion object {
const val TASKBAR_BOTTOM_SPACE = 5
+ const val HOTSEAT_VERTICAL_CENTER = 95
const val BUBBLE_BAR_WIDTH = 200
const val BUBBLE_BAR_HEIGHT = 100
const val HOTSEAT_TRANSLATION_Y = -45f
@@ -108,6 +109,7 @@
setUpStashedHandleView()
setUpBubbleStashedHandleViewController()
PhysicsAnimatorTestUtils.prepareForTest()
+ mTransientBubbleStashController.setHotseatVerticalCenter(HOTSEAT_VERTICAL_CENTER)
mTransientBubbleStashController.init(
taskbarInsetsController,
bubbleBarViewController,
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 177b28c..5f5fb72 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -50,6 +50,8 @@
// automatically when user interacts with the launcher.
public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+ private boolean mIsThemeUpdatedBeforeRecreate;
+
private ActionMode mCurrentActionMode;
private int mThemeRes = R.style.AppTheme;
@@ -80,8 +82,13 @@
updateTheme();
}
- private void updateTheme() {
+ public boolean isThemeUpdatedBeforeRecreate() {
+ return mIsThemeUpdatedBeforeRecreate;
+ }
+
+ protected void updateTheme() {
if (mThemeRes != Themes.getActivityThemeRes(this)) {
+ mIsThemeUpdatedBeforeRecreate = true;
recreate();
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8862550..4305703 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.Utilities.isEnglishLanguage;
import static com.android.launcher3.Utilities.pxFromSp;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
@@ -1344,8 +1345,14 @@
}
if ((Flags.enableTwolineToggle()
&& LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context))) {
- // Add extra textHeight to the existing allAppsCellHeight.
- allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+ if (!isEnglishLanguage(context)) {
+ // Set toggle preference value to false if not english here as it's possible the
+ // preference is stale after language change.
+ LauncherPrefs.get(context).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, false);
+ } else {
+ // Add extra textHeight to the existing allAppsCellHeight.
+ allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+ }
}
updateHotseatSizes(iconSizePx);
@@ -2019,6 +2026,18 @@
}
/**
+ * Returns the number of pixels the hotseat icons vertical center is translated from the bottom
+ * of the screen.
+ */
+ public int getHotseatVerticalCenter() {
+ return hotseatBarSizePx
+ - (isQsbInline ? 0 : hotseatQsbVisualHeight)
+ - hotseatQsbSpace
+ - (hotseatCellHeightPx / 2)
+ + ((hotseatCellHeightPx - iconSizePx) / 2);
+ }
+
+ /**
* Returns the number of pixels the taskbar is translated from the bottom of the screen.
*/
public int getTaskbarOffsetY() {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9192e13..71a2589 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -616,6 +616,14 @@
}
/**
+ * Utility method to know if a device's primary language is English.
+ */
+ public static boolean isEnglishLanguage(Context context) {
+ return context.getResources().getConfiguration().locale.getLanguage()
+ .equals(Locale.ENGLISH.getLanguage());
+ }
+
+ /**
* Returns the full drawable for info as multiple layers of AdaptiveIconDrawable. The second
* drawable in the Pair is the badge used with the icon.
*