Added BubbleBarLocation drop target.
A drop target for the BubbleBarLocation has been added. This
implementation enables the Launcher process to handle application
drag-and-drop actions onto the bubble bar.
Known issues:
1) Upon dropping, the dragged icon animates back to its original taskbar
position from the top-left corner.
2) The expanded view drop target is not presented.
3) When an icon is dropped on the opposite side of the bubble bar, the
resulting location does not change, even though the drop target is
correctly displayed.
Bug: 397459664
Flag: com.android.wm.shell.enable_create_any_bubble
Test: Manual. Navigate to the overview screen, drag a taskbar icon to
the left or right side, targeting the bubble bar drop zone. Verify the
drop target's appearance. Then, drop the icon and observe that a new
application bubble is added.
Change-Id: I0f64a664620959f2503ac3fd711476b354a09348
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index d531e2c..9b7e320 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -68,6 +68,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logging.StatsLogManager;
@@ -765,8 +766,11 @@
@Override
public void addDropTarget(DropTarget target) {
- // No-op as Taskbar currently doesn't support any drop targets internally.
- // Note: if we do add internal DropTargets, we'll still need to ignore Folder.
+ if (target instanceof Folder) {
+ // we need to ignore Folder.
+ return;
+ }
+ super.addDropTarget(target);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
new file mode 100644
index 0000000..383f4d2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.bubbles
+
+import android.graphics.Rect
+import android.view.View
+import com.android.launcher3.DropTarget
+import com.android.launcher3.dragndrop.DragOptions
+import com.android.launcher3.model.data.ItemInfo
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+
+/**
+ * Implementation of the {@link DropTarget} that handles drag and drop events over the bubble bar
+ * locations.
+ */
+class BubbleBarLocationDropTarget(
+ private val bubbleBarLocation: BubbleBarLocation,
+ private val bubbleBarDragListener: BubbleBarDragListener,
+) : DropTarget {
+
+ /** Controller that takes care of the bubble bar drag events inside launcher process. */
+ interface BubbleBarDragListener {
+
+ /** Called when the drag event is over the bubble bar drop zone. */
+ fun onLauncherItemDraggedOverBubbleBarDragZone(location: BubbleBarLocation)
+
+ /** Called when the drag event leaves the bubble bar drop zone. */
+ fun onLauncherItemDraggedOutsideBubbleBarDropZone()
+
+ /** Called when the drop event happens over the bubble bar drop zone. */
+ fun onLauncherItemDroppedOverBubbleBarDragZone(
+ location: BubbleBarLocation,
+ itemInfo: ItemInfo,
+ )
+
+ /** Gets the hit [rect][android.graphics.Rect] of the bubble bar location. */
+ fun getBubbleBarLocationHitRect(bubbleBarLocation: BubbleBarLocation, outRect: Rect)
+
+ /** Provides the view that will accept the drop. */
+ fun getDropView(): View
+ }
+
+ private var isShowingDropTarget = false
+
+ override fun isDropEnabled(): Boolean = true
+
+ override fun onDrop(dragObject: DropTarget.DragObject, options: DragOptions) {
+ val itemInfo = dragObject.dragInfo ?: return
+ // TODO(b/397459664) : fix task bar icon animation after drop
+ // TODO(b/397459664) : update bubble bar location
+ bubbleBarDragListener.onLauncherItemDroppedOverBubbleBarDragZone(
+ bubbleBarLocation,
+ itemInfo,
+ )
+ }
+
+ override fun onDragEnter(dragObject: DropTarget.DragObject) {}
+
+ override fun onDragOver(dragObject: DropTarget.DragObject) {
+ if (isShowingDropTarget) return
+ isShowingDropTarget = true
+ bubbleBarDragListener.onLauncherItemDraggedOverBubbleBarDragZone(bubbleBarLocation)
+ }
+
+ override fun onDragExit(dragObject: DropTarget.DragObject) {
+ // TODO(b/397459664) : fix the issue for no bubbles, when moving task bar icon out of
+ // the bubble bar drag zone drag ends and swipes gesture swipes the overview
+ if (!isShowingDropTarget) return
+ isShowingDropTarget = false
+ bubbleBarDragListener.onLauncherItemDraggedOutsideBubbleBarDropZone()
+ }
+
+ override fun acceptDrop(dragObject: DropTarget.DragObject): Boolean = true
+
+ override fun prepareAccessibilityDrop() {}
+
+ override fun getHitRectRelativeToDragLayer(outRect: Rect) {
+ bubbleBarDragListener.getBubbleBarLocationHitRect(bubbleBarLocation, outRect)
+ }
+
+ override fun getDropView(): View = bubbleBarDragListener.getDropView()
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index b90a5b0..beda30c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -24,6 +24,8 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -43,11 +45,15 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.bubbles.BubbleBarLocationDropTarget.BubbleBarDragListener;
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
@@ -59,6 +65,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
import java.io.PrintWriter;
import java.util.List;
@@ -117,6 +124,61 @@
updateTranslationY();
setBubbleBarScaleAndPadding(pinningProgress);
});
+ private final BubbleBarDragListener mDragListener = new BubbleBarDragListener() {
+
+ @NonNull
+ @Override
+ public void getBubbleBarLocationHitRect(@NonNull BubbleBarLocation bubbleBarLocation,
+ Rect outRect) {
+ Point screenSize = DisplayController.INSTANCE.get(mActivity).getInfo().currentSize;
+ outRect.top = screenSize.y - mBubbleBarDropTargetSize;
+ outRect.bottom = screenSize.y;
+ if (bubbleBarLocation.isOnLeft(mBarView.isLayoutRtl())) {
+ outRect.left = 0;
+ outRect.right = mBubbleBarDropTargetSize;
+ } else {
+ outRect.left = screenSize.x - mBubbleBarDropTargetSize;
+ outRect.right = screenSize.x;
+ }
+ }
+
+ @Override
+ public void onLauncherItemDroppedOverBubbleBarDragZone(@NonNull BubbleBarLocation location,
+ @NonNull ItemInfo itemInfo) {
+ //TODO(b/397459664) : fix task bar icon animation after drop
+ //TODO(b/397459664) : update bubble bar location
+ ShortcutInfo shortcutInfo = null;
+ if (itemInfo instanceof WorkspaceItemInfo) {
+ shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
+ }
+ Intent itemIntent = itemInfo.getIntent();
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
+ if (shortcutInfo != null) {
+ systemUiProxy.showShortcutBubble(shortcutInfo);
+ } else if (itemIntent != null && itemIntent.getComponent() != null) {
+ systemUiProxy.showAppBubble(itemIntent, itemInfo.user);
+ }
+ }
+
+ @Override
+ public void onLauncherItemDraggedOutsideBubbleBarDropZone() {
+ //TODO(b/397459664) : hide expanded view drop target
+ onItemDraggedOutsideBubbleBarDropZone();
+ }
+
+ @Override
+ public void onLauncherItemDraggedOverBubbleBarDragZone(
+ @NonNull BubbleBarLocation location) {
+ //TODO(b/397459664) : show expanded view drop target
+ onDragItemOverBubbleBarDragZone(location);
+ }
+
+ @NonNull
+ @Override
+ public View getDropView() {
+ return mBarView;
+ }
+ };
// Modified when swipe up is happening on the bubble bar or task bar.
private float mBubbleBarSwipeUpTranslationY;
@@ -139,8 +201,12 @@
private BubbleBarFlyoutController mBubbleBarFlyoutController;
private BubbleBarPinController mBubbleBarPinController;
private TaskbarSharedState mTaskbarSharedState;
+ private TaskbarDragController mTaskbarDragController;
+ private final BubbleBarLocationDropTarget mBubbleBarLeftDropTarget;
+ private final BubbleBarLocationDropTarget mBubbleBarRightDropTarget;
private final TimeSource mTimeSource = System::currentTimeMillis;
private final int mTaskbarTranslationDelta;
+ private final int mBubbleBarDropTargetSize;
@Nullable
private BubbleBarBoundsChangeListener mBoundsChangeListener;
@@ -158,11 +224,21 @@
R.dimen.bubblebar_transient_taskbar_min_distance);
mDragElevation = res.getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
+ if (DeviceConfig.isSmallTablet(mActivity)) {
+ mBubbleBarDropTargetSize = res.getDimensionPixelSize(R.dimen.drag_zone_bubble_fold);
+ } else {
+ mBubbleBarDropTargetSize = res.getDimensionPixelSize(R.dimen.drag_zone_bubble_tablet);
+ }
+ mBubbleBarLeftDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.LEFT,
+ mDragListener);
+ mBubbleBarRightDropTarget = new BubbleBarLocationDropTarget(BubbleBarLocation.RIGHT,
+ mDragListener);
}
/** Initializes controller. */
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
+ mTaskbarDragController = controllers.taskbarDragController;
mTaskbarSharedState = controllers.getSharedState();
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
@@ -264,6 +340,8 @@
mBubbleBarController.updateBubbleBarLocation(location, source);
}
};
+ mTaskbarDragController.addDropTarget(mBubbleBarLeftDropTarget);
+ mTaskbarDragController.addDropTarget(mBubbleBarRightDropTarget);
}
/** Returns animated float property responsible for pinning transition animation. */
@@ -542,7 +620,9 @@
*/
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
mBarView.showDropTarget(/* isDropTarget = */ true);
- mIsLocationUpdatedForDropTarget = getBubbleBarLocation() != bubbleBarLocation;
+ boolean isRtl = mBarView.isLayoutRtl();
+ mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl)
+ != bubbleBarLocation.isOnLeft(isRtl);
if (mIsLocationUpdatedForDropTarget) {
animateBubbleBarLocation(bubbleBarLocation);
}
@@ -1278,6 +1358,8 @@
/** Called when the controller is destroyed. */
public void onDestroy() {
adjustTaskbarAndHotseatToBubbleBarState(/*isBubbleBarExpanded = */false);
+ mTaskbarDragController.removeDropTarget(mBubbleBarLeftDropTarget);
+ mTaskbarDragController.removeDropTarget(mBubbleBarRightDropTarget);
}
/**
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 2d99510..7f0c7b5 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.Rect;
+import android.view.View;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -145,4 +146,9 @@
// These methods are implemented in Views
void getHitRectRelativeToDragLayer(Rect outRect);
+
+ /** Returns the drop target view. By default, the implementor class is cast to the view. */
+ default View getDropView() {
+ return (View) this;
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index c50c008..613b430 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -558,7 +558,7 @@
target.getHitRectRelativeToDragLayer(r);
if (r.contains(x, y)) {
- mActivity.getDragLayer().mapCoordInSelfToDescendant((View) target,
+ mActivity.getDragLayer().mapCoordInSelfToDescendant(target.getDropView(),
mCoordinatesTemp);
mDragObject.x = mCoordinatesTemp[0];
mDragObject.y = mCoordinatesTemp[1];