Merge "Notify taskbar and nav bar of bubble bar location updates." into main

This commit is contained in:
Mykola Podolian
2025-05-29 21:12:09 -07:00
committed by Android (Google) Code Review
4 changed files with 198 additions and 19 deletions

View File

@@ -565,6 +565,7 @@ public class BubbleBarViewController {
mBarView.showDropTarget(showingDropTarget);
}
//TODO(b/411505605) remove unused IPC calls and code
/**
* Notifies the controller that a drag event is over the Bubble Bar drop zone. The controller
* will display the appropriate drop target and enter drop target mode. The controller will also

View File

@@ -140,6 +140,7 @@ public class BubbleControllers {
return -(int) bubbleStashController.getBubbleBarTranslationY();
}
},
bubbleBarLocationListeners,
SystemUiProxy.INSTANCE.get(taskbarControllers.taskbarActivityContext));
mPostInitRunnables.executeAllAndDestroy();
}

View File

@@ -25,6 +25,7 @@ import com.android.launcher3.dragndrop.DragController
import com.android.launcher3.dragndrop.DragOptions
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener
import com.android.launcher3.taskbar.bubbles.BubbleBarLocationDropTarget.BubbleBarDropTargetController
import com.android.quickstep.SystemUiProxy
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
@@ -47,10 +48,12 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
DragController.DragListener {
@VisibleForTesting val dropTargetManager: DropTargetManager
@VisibleForTesting lateinit var bubbleBarLeftDropTarget: BubbleBarLocationDropTarget
@VisibleForTesting lateinit var bubbleBarRightDropTarget: BubbleBarLocationDropTarget
@VisibleForTesting lateinit var dragZoneFactory: DragZoneFactory
// If item drop is handled the next sysui update will set the bubble bar location
@VisibleForTesting var isItemDropHandled = false
private lateinit var bubbleBarLocationListener: BubbleBarLocationListener
private lateinit var systemUiProxy: SystemUiProxy
private lateinit var bubbleBarViewController: BubbleBarViewController
@@ -61,10 +64,12 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
fun init(
bubbleBarViewController: BubbleBarViewController,
bubbleBarPropertiesProvider: BubbleBarPropertiesProvider,
bubbleBarLocationListener: BubbleBarLocationListener,
systemUiProxy: SystemUiProxy,
) {
this.bubbleBarViewController = bubbleBarViewController
this.systemUiProxy = systemUiProxy
this.bubbleBarLocationListener = bubbleBarLocationListener
val dropController: BubbleBarDropTargetController = createDropController()
dragZoneFactory = createDragZoneFactory(bubbleBarPropertiesProvider)
bubbleBarLeftDropTarget = createDropTarget(dropController, isLeftDropTarget = true)
@@ -95,6 +100,7 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
}
override fun onDragStart(dragObject: DragObject, options: DragOptions) {
isItemDropHandled = false
val launcherIcon: DraggedObject = LauncherIcon(bubbleBarViewController.hasBubbles()) {}
val dragZones: List<DragZone> = dragZoneFactory.createSortedDragZones(launcherIcon)
dropTargetManager.onDragStarted(launcherIcon, dragZones)
@@ -110,8 +116,17 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
context.isRtl,
object : DragToBubblesZoneChangeListener.Callback {
private var currentBarLocation: BubbleBarLocation? = null
override fun onDragEnteredLocation(bubbleBarLocation: BubbleBarLocation?) {
bubbleBarViewController.isShowingDropTarget = bubbleBarLocation != null
if (isItemDropHandled) return
val updatedLocation = bubbleBarLocation ?: getStartingBubbleBarLocation()
currentBarLocation = currentBarLocation ?: getStartingBubbleBarLocation()
if (updatedLocation != currentBarLocation) {
currentBarLocation = updatedLocation
bubbleBarLocationListener.onBubbleBarLocationAnimated(updatedLocation)
}
}
override fun getStartingBubbleBarLocation(): BubbleBarLocation {
@@ -122,14 +137,9 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
override fun hasBubbles(): Boolean = bubbleBarViewController.hasBubbles()
override fun animateBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {
if (isItemDropHandled) return
bubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation)
}
override fun bubbleBarPillowShownAtLocation(
bubbleBarLocation: BubbleBarLocation?
) {
// TODO(b/411506181) adjust taskbar
}
},
)
return DropTargetManager(context, bubbleBarContainer, listener)
@@ -154,6 +164,18 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
private fun createDropController(): BubbleBarDropTargetController {
return object : BubbleBarDropTargetController {
override fun onDrop(itemInfo: ItemInfo, isLeftDropTarget: Boolean) {
isItemDropHandled = handleDrop(itemInfo, isLeftDropTarget)
}
override fun acceptDrop(itemInfo: ItemInfo): Boolean {
return hasShortcutInfo(itemInfo) || itemInfo.intent?.component != null
}
fun hasShortcutInfo(itemInfo: ItemInfo): Boolean {
return itemInfo is WorkspaceItemInfo && itemInfo.deepShortcutInfo != null
}
private fun handleDrop(itemInfo: ItemInfo, isLeftDropTarget: Boolean): Boolean {
val location =
if (isLeftDropTarget) {
BubbleBarLocation.LEFT
@@ -163,20 +185,13 @@ class DragToBubbleController(private val context: Context, bubbleBarContainer: F
if (hasShortcutInfo(itemInfo)) {
val si = (itemInfo as WorkspaceItemInfo).deepShortcutInfo
systemUiProxy.showShortcutBubble(si, location)
return
return true
}
val itemIntent: Intent = itemInfo.intent ?: return
val packageName = itemIntent.component?.packageName ?: return
val itemIntent: Intent = itemInfo.intent ?: return false
val packageName = itemIntent.component?.packageName ?: return false
itemIntent.setPackage(packageName)
systemUiProxy.showAppBubble(itemIntent, itemInfo.user, location)
}
override fun acceptDrop(itemInfo: ItemInfo): Boolean {
return hasShortcutInfo(itemInfo) || itemInfo.intent?.component != null
}
fun hasShortcutInfo(itemInfo: ItemInfo): Boolean {
return itemInfo is WorkspaceItemInfo && itemInfo.deepShortcutInfo != null
return true
}
}
}

View File

@@ -31,6 +31,7 @@ import com.android.launcher3.DropTarget
import com.android.launcher3.DropTarget.DragObject
import com.android.launcher3.dragndrop.DragOptions
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.taskbar.bubbles.BubbleBarController.BubbleBarLocationListener
import com.android.quickstep.SystemUiProxy
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DeviceConfig
@@ -44,7 +45,9 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
@@ -61,6 +64,7 @@ class DragToBubbleControllerTest {
private val container = FrameLayout(context)
private val bubbleBarViewController: BubbleBarViewController = mock()
private val systemUiProxy: SystemUiProxy = mock()
private val bubbleBarLocationListener: BubbleBarLocationListener = mock()
private val bubbleBarPropertiesProvider = FakeBubbleBarPropertiesProvider()
private val testDragZonesFactory = createTestDragZoneFactory()
private val dragObject = DragObject(context)
@@ -86,10 +90,12 @@ class DragToBubbleControllerTest {
@Before
fun setUp() {
prepareBubbleBarViewController()
dragToBubbleController = DragToBubbleController(context, container)
dragToBubbleController.init(
bubbleBarViewController,
bubbleBarPropertiesProvider,
bubbleBarLocationListener,
systemUiProxy,
)
dragToBubbleController.dragZoneFactory = testDragZonesFactory
@@ -104,6 +110,7 @@ class DragToBubbleControllerTest {
assertThat(secondDropTargetView!!.parent).isEqualTo(container)
assertThat(dropTargetView.alpha).isEqualTo(0f)
assertThat(secondDropTargetView!!.alpha).isEqualTo(0f)
assertThat(dragToBubbleController.isItemDropHandled).isFalse()
}
@Test
@@ -235,13 +242,168 @@ class DragToBubbleControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(leftDropTargetRect)
bubbleBarLeftDropTarget.onDragEnter(dragObject)
bubbleBarLeftDropTarget.onDragExit(dragObject)
bubbleBarLeftDropTarget.onDrop(dragObject, DragOptions())
assertThat(dragToBubbleController.isItemDropHandled).isTrue()
bubbleBarLeftDropTarget.onDragExit(dragObject)
}
verify(systemUiProxy).showAppBubble(itemIntent, appInfo.user, BubbleBarLocation.LEFT)
}
@Test
fun dragExitRightZone_noBubbles_listenerNotNotified() {
// Scenario: No bubbles. Drag enters RIGHT, then exits to no particular zone.
// This is distinct as it starts on the default side.
prepareBubbleBarViewController(
hasBubbles = false,
bubbleBarLocation = BubbleBarLocation.RIGHT,
)
dragToBubbleController.onDragStart(dragObject, DragOptions())
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(rightDropTargetRect)
bubbleBarRightDropTarget.onDragEnter(dragObject) // Location is the same
}
verify(bubbleBarLocationListener, never())
.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXY(0, 0) // Move out of all zones
bubbleBarRightDropTarget.onDragExit(dragObject)
}
// Exiting the RIGHT zone (which is the default) should not re-notify of RIGHT
verify(bubbleBarLocationListener, never())
.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
}
@Test
fun onDragEnd_noBubbles_wasDraggingLeft_listenerNotifiedWithDefaultRightLocationAnimated() {
val startingLocation = BubbleBarLocation.RIGHT
// Scenario: No bubbles. Drag was over LEFT zone. Drag ends.
prepareBubbleBarViewController(hasBubbles = false, bubbleBarLocation = startingLocation)
dragToBubbleController.onDragStart(dragObject, DragOptions())
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(leftDropTargetRect)
bubbleBarLeftDropTarget.onDragEnter(dragObject)
}
// Notifies onBubbleBarLocationAnimated(LEFT)
verify(bubbleBarLocationListener).onBubbleBarLocationAnimated(BubbleBarLocation.LEFT)
clearInvocations(bubbleBarLocationListener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragToBubbleController.onDragEnd()
}
assertThat(dragToBubbleController.isItemDropHandled).isFalse()
// After drag ends (and no bubbles), the listener should be notified of the default location
verify(bubbleBarLocationListener).onBubbleBarLocationAnimated(startingLocation)
}
@Test
fun onDragEnd_noBubbles_wasDraggingRight_listenerNotifiedWithDefaultRightLocationAnimated() {
// Scenario: No bubbles. Drag was over RIGHT zone (default side). Drag ends.
prepareBubbleBarViewController(hasBubbles = false)
dragToBubbleController.onDragStart(dragObject, DragOptions())
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(rightDropTargetRect)
}
verify(bubbleBarLocationListener, never())
.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
clearInvocations(bubbleBarLocationListener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXY(0, 0)
bubbleBarLeftDropTarget.onDragExit(dragObject)
dragToBubbleController.onDragEnd()
}
// After drag ends (and no bubbles), listener should not be notified of the default
// location.
verify(bubbleBarLocationListener, never())
.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
}
@Test
fun dragEnterLeftZone_bubblesOnLeft_listenerNotNotified() {
// Scenario: Bubbles on LEFT. Drag enters LEFT zone.
prepareBubbleBarViewController(
hasBubbles = true,
bubbleBarLocation = BubbleBarLocation.LEFT,
)
dragToBubbleController.onDragStart(dragObject, DragOptions())
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(leftDropTargetRect)
bubbleBarLeftDropTarget.onDragEnter(dragObject)
}
// Bubbles are already on the LEFT, and drag enters LEFT.
// No new animation to LEFT should be triggered by the zone entry itself.
verify(bubbleBarLocationListener, never())
.onBubbleBarLocationAnimated(BubbleBarLocation.LEFT)
}
@Test
fun onDragEnd_bubblesOnLeft_defaultIsLeft_wasDraggingRight_listenerNotifiedLeftAnimated() {
// Scenario: Bubbles on LEFT. Drag was over RIGHT zone. Drag ends.
prepareBubbleBarViewController(
hasBubbles = true,
bubbleBarLocation = BubbleBarLocation.LEFT,
)
dragToBubbleController.onDragStart(dragObject, DragOptions())
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXYToCenterOf(rightDropTargetRect)
bubbleBarRightDropTarget.onDragEnter(dragObject) // Notifies Animated(RIGHT)
}
verify(bubbleBarLocationListener).onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
clearInvocations(bubbleBarLocationListener) // Clear the Animated(RIGHT)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragObject.updateXY(0, 0)
bubbleBarLeftDropTarget.onDragExit(dragObject)
dragToBubbleController.onDragEnd()
}
// Bubble bar's final animated location should be LEFT.
verify(bubbleBarLocationListener).onBubbleBarLocationAnimated(BubbleBarLocation.LEFT)
}
@Test
fun dragEnterLeftThenExitToNoZoneThenEnterRight_noBubbles_listenerSequenceCorrectAnimated() {
// Scenario: No bubbles. Complex drag path: Left -> None -> Right
prepareBubbleBarViewController(hasBubbles = false)
dragToBubbleController.onDragStart(dragObject, DragOptions())
clearInvocations(bubbleBarLocationListener)
val inOrder = inOrder(bubbleBarLocationListener)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
// 1. Enter Left
dragObject.updateXYToCenterOf(leftDropTargetRect)
bubbleBarLeftDropTarget.onDragEnter(dragObject)
// 2. Exit Left to no zone
dragObject.updateXY(0, 0)
bubbleBarLeftDropTarget.onDragExit(dragObject)
// 3. Enter Right
dragObject.updateXYToCenterOf(rightDropTargetRect)
bubbleBarRightDropTarget.onDragEnter(dragObject)
}
inOrder
.verify(bubbleBarLocationListener)
.onBubbleBarLocationAnimated(BubbleBarLocation.LEFT)
// Revert to default, following enter of the same zone should not trigger updated
inOrder
.verify(bubbleBarLocationListener)
.onBubbleBarLocationAnimated(BubbleBarLocation.RIGHT)
}
private fun prepareBubbleBarViewController(
hasBubbles: Boolean = false,
bubbleBarLocation: BubbleBarLocation = BubbleBarLocation.RIGHT,