Merge "pin-shortcut: Tracks hotseat states and adds unpin option for taskbar" into main
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 45142b8..65f4b3c 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -375,4 +375,9 @@
<string name="header_default_app_title">App title</string>
<!-- Content description for the header close button. [CHAR LIMIT=NONE] -->
<string name="header_close_icon_description">Close button</string>
+
+ <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
+ <string name="pin_to_taskbar">Pin to taskbar</string>
+ <!-- Label for unpinning an item from the taskbar. [CHAR_LIMIT=20] -->
+ <string name="unpin_from_taskbar">Unpin from taskbar</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt
new file mode 100644
index 0000000..b9a211d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/PinToTaskbarShortcut.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 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.view.View
+import com.android.launcher3.R
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * A single menu item shortcut to allow users to pin an item to the taskbar and unpin an item from
+ * the taskbar.
+ */
+class PinToTaskbarShortcut<T>(target: T, itemInfo: ItemInfo?, originalView: View, isPin: Boolean) :
+ SystemShortcut<T>(
+ if (isPin) R.drawable.ic_pin else R.drawable.ic_unpin,
+ if (isPin) R.string.pin_to_taskbar else R.string.unpin_from_taskbar,
+ target,
+ itemInfo,
+ originalView,
+ ) where T : Context?, T : ActivityContext? {
+
+ override fun onClick(v: View?) {
+ // TODO(b/375648361): Pin/Unpin the item here.
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 0fa82ae..d4ad555 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -204,6 +204,7 @@
ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
mContainer.updateItems(hotseatItemInfos, recentTasks);
mControllers.taskbarViewController.updateIconViewsRunningStates();
+ mControllers.taskbarPopupController.setHotseatInfosList(mHotseatItems);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index a9ee584..e81563e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -16,18 +16,20 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
@@ -81,6 +83,8 @@
private TaskbarControllers mControllers;
private boolean mAllowInitialSplitSelection;
private AppInfo[] mAppInfosList;
+ // Saves the ItemInfos in the hotseat without the predicted items.
+ private SparseArray<ItemInfo> mHotseatInfosList;
private ManageWindowsTaskbarShortcut<BaseTaskbarContext> mManageWindowsTaskbarShortcut;
@@ -149,6 +153,14 @@
.filter(Objects::nonNull)
.collect(Collectors.toList());
+ // TODO(b/375648361): Revisit to see if this can be implemented within getSystemShortcuts().
+ if (Flags.enablePinningAppWithContextMenu()) {
+ SystemShortcut shortcut = createPinShortcut(context, item, icon);
+ if (shortcut != null) {
+ systemShortcuts.add(0, shortcut);
+ }
+ }
+
container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
R.layout.popup_container, context.getDragLayer(), false);
container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
@@ -172,9 +184,6 @@
// append split options to APP_INFO shortcut if not in Desktop Windowing mode, the order
// here will reflect in the popup
ArrayList<SystemShortcut.Factory> shortcuts = new ArrayList<>();
- if (Flags.enablePinningAppWithContextMenu()) {
- shortcuts.add(PIN_UNPIN_ITEM);
- }
shortcuts.add(APP_INFO);
if (!mControllers.taskbarDesktopModeController
.isInDesktopModeAndNotInOverview(mContext.getDisplayId())) {
@@ -193,6 +202,24 @@
return shortcuts.stream();
}
+ @Nullable
+ private SystemShortcut createPinShortcut(BaseTaskbarContext target, ItemInfo itemInfo,
+ BubbleTextView originalView) {
+ // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut to pin.
+ if (itemInfo.isPredictedItem()) {
+ return null;
+ }
+ if (itemInfo.container == CONTAINER_HOTSEAT) {
+ return new PinToTaskbarShortcut<>(target, itemInfo, originalView, false);
+ }
+ if (mHotseatInfosList.size()
+ < mContext.getTaskbarSpecsEvaluator().getNumShownHotseatIcons()) {
+ return new PinToTaskbarShortcut<>(target, itemInfo, originalView, true);
+ }
+
+ return null;
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarPopupController:");
@@ -276,6 +303,10 @@
return index < 0 ? null : mAppInfosList[index];
}
+ public void setHotseatInfosList(SparseArray<ItemInfo> info) {
+ mHotseatInfosList = info;
+ }
+
/**
* Returns a stream of Multi Instance menu options if an app supports it.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
index 822ca64..f1ed6c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt
@@ -26,6 +26,8 @@
numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns,
) {
var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numColumns, numRows)
+ val numShownHotseatIcons
+ get() = taskbarActivityContext.deviceProfile.numShownHotseatIcons
// TODO(b/341146605) : initialize it to taskbar container in later cl.
private var taskbarContainer: List<TaskbarContainer> = emptyList()
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index aab8ad1..484978c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -48,7 +48,6 @@
import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT;
import static com.android.launcher3.popup.SystemShortcut.DONT_SUGGEST_APP;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
-import static com.android.launcher3.popup.SystemShortcut.PIN_UNPIN_ITEM;
import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
import static com.android.launcher3.popup.SystemShortcut.UNINSTALL_APP;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -475,9 +474,6 @@
List<SystemShortcut.Factory> shortcuts = new ArrayList(Arrays.asList(
APP_INFO, WellbeingModel.SHORTCUT_FACTORY, mHotseatPredictionController));
- if (Flags.enablePinningAppWithContextMenu()) {
- shortcuts.add(0, PIN_UNPIN_ITEM);
- }
shortcuts.addAll(getSplitShortcuts());
shortcuts.add(WIDGETS);
shortcuts.add(INSTALL);
diff --git a/res/drawable/ic_unpin.xml b/res/drawable/ic_unpin.xml
new file mode 100644
index 0000000..557b4f9
--- /dev/null
+++ b/res/drawable/ic_unpin.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <group>
+ <clip-path
+ android:pathData="M0,0h24v24h-24z"/>
+ <path
+ android:pathData="M17,3V5H16V13.175L14,11.175V5H10V7.175L7.825,5L7,4.175V3H17ZM12,23L11,22V16H6V14L8,12V10.85L1.4,4.2L2.8,2.8L21.2,21.2L19.75,22.6L13.15,16H13V22L12,23ZM8.85,14H11.15L10.05,12.9L10,12.85L8.85,14Z"
+ android:fillColor="#FF000000"/>
+ </group>
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 56befd6..db87686 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -235,8 +235,6 @@
<string name="pin_prediction">Pin Prediction</string>
<!-- Label for bubbling a launcher item. [CHAR_LIMIT=20] -->
<string name="bubble">Bubble</string>
- <!-- Label for pinning an item to the taskbar. [CHAR_LIMIT=20] -->
- <string name="pin_to_taskbar">Pin to taskbar</string>
<!-- Permissions: -->
<skip />
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 7e08c6e..b7efdec 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -210,30 +210,6 @@
}
}
- public static final Factory<ActivityContext> PIN_UNPIN_ITEM =
- (context, itemInfo, originalView) -> {
- // Predicted items use {@code HotseatPredictionController.PinPrediction} shortcut
- // to pin.
- if (itemInfo.isPredictedItem()) {
- return null;
- }
- return new PinUnpinItem<>(context, itemInfo, originalView);
- };
-
- private static class PinUnpinItem<T extends ActivityContext> extends SystemShortcut<T> {
- PinUnpinItem(T target, ItemInfo itemInfo, @NonNull View originalView) {
- // TODO(b/375648361): Check the pin state of the item to determine if the pin or the
- // unpin option should be used.
- super(R.drawable.ic_pin, R.string.pin_to_taskbar, target,
- itemInfo, originalView);
- }
-
- @Override
- public void onClick(View view) {
- // TODO(b/375648361): Pin/Unpin the item here.
- }
- }
-
public static final Factory<ActivityContext> PRIVATE_PROFILE_INSTALL =
(context, itemInfo, originalView) -> {
if (originalView == null) {