Allow updating the flyout message
Add functions to allow updating the contents of the flyout while it
is either expanding or fully expanded.
Flag: com.android.wm.shell.enable_bubble_bar
Bug: 277815200
Test: atest BubbleBarFlyoutControllerTest
Change-Id: Iea3fc7df792e02605df6b44c1da39e267f6d9d43
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 fdbbbb0..f389d7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -72,32 +72,63 @@
lp.marginEnd = horizontalMargin
container.addView(flyout, lp)
+ this.flyout = flyout
+ flyout.showFromCollapsed(message) { showFlyout(AnimationType.COLLAPSE, onEnd) }
+ }
+
+ private fun showFlyout(animationType: AnimationType, endAction: () -> Unit) {
+ val flyout = this.flyout ?: return
val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(ANIMATION_DURATION_MS)
- animator.addUpdateListener { _ ->
- flyout.updateExpansionProgress(animator.animatedValue as Float)
+ when (animationType) {
+ AnimationType.FADE ->
+ animator.addUpdateListener { _ -> flyout.alpha = animator.animatedValue as Float }
+ AnimationType.COLLAPSE ->
+ animator.addUpdateListener { _ ->
+ flyout.updateExpansionProgress(animator.animatedValue as Float)
+ }
}
animator.addListener(
- onStart = {
- 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) callbacks.extendTopBoundary(space = -flyoutTop.toInt())
- },
+ onStart = { extendTopBoundary() },
onEnd = {
- onEnd()
+ endAction()
flyout.setOnClickListener { callbacks.flyoutClicked() }
},
)
- flyout.showFromCollapsed(message) { animator.start() }
- this.flyout = flyout
+ animator.start()
+ }
+
+ fun updateFlyoutFullyExpanded(message: BubbleBarFlyoutMessage, onEnd: () -> Unit) {
+ val flyout = flyout ?: return
+ hideFlyout(AnimationType.FADE) {
+ flyout.updateData(message) { showFlyout(AnimationType.FADE, onEnd) }
+ }
+ }
+
+ fun updateFlyoutWhileExpanding(message: BubbleBarFlyoutMessage) {
+ val flyout = flyout ?: return
+ flyout.updateData(message) { extendTopBoundary() }
+ }
+
+ private fun extendTopBoundary() {
+ val flyout = flyout ?: return
+ 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) callbacks.extendTopBoundary(space = -flyoutTop.toInt())
}
fun cancelFlyout(endAction: () -> Unit) {
- hideFlyout(AnimationType.FADE, endAction)
+ hideFlyout(AnimationType.FADE) {
+ cleanupFlyoutView()
+ endAction()
+ }
}
fun collapseFlyout(endAction: () -> Unit) {
- hideFlyout(AnimationType.COLLAPSE, endAction)
+ hideFlyout(AnimationType.COLLAPSE) {
+ cleanupFlyoutView()
+ endAction()
+ }
}
private fun hideFlyout(animationType: AnimationType, endAction: () -> Unit) {
@@ -112,15 +143,15 @@
flyout.updateExpansionProgress(animator.animatedValue as Float)
}
}
- animator.addListener(
- onStart = { flyout.setOnClickListener(null) },
- onEnd = {
- container.removeView(flyout)
- this@BubbleBarFlyoutController.flyout = null
- callbacks.resetTopBoundary()
- endAction()
- },
- )
+ animator.addListener(onStart = { flyout.setOnClickListener(null) }, onEnd = { endAction() })
animator.start()
}
+
+ private fun cleanupFlyoutView() {
+ container.removeView(flyout)
+ this@BubbleBarFlyoutController.flyout = null
+ callbacks.resetTopBoundary()
+ }
+
+ fun hasFlyout() = flyout != null
}
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 bb8a392..af8aaf8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -198,6 +198,8 @@
} else {
-positioner.distanceToCollapsedPosition.x
}
+ // TODO: b/277815200 - before collapsing, recalculate translationToCollapsedPosition because
+ // the collapsed position may have changed
val tyToCollapsedPosition =
positioner.distanceToCollapsedPosition.y + triangleHeight - triangleOverlap
translationToCollapsedPosition = PointF(txToCollapsedPosition, tyToCollapsedPosition)
@@ -217,6 +219,12 @@
scheduler.runAfterLayout(expandAnimation)
}
+ /** Updates the content of the flyout and schedules [afterLayout] to run after a layout pass. */
+ fun updateData(flyoutMessage: BubbleBarFlyoutMessage, afterLayout: () -> Unit) {
+ setData(flyoutMessage)
+ scheduler.runAfterLayout(afterLayout)
+ }
+
private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
if (flyoutMessage.icon != null) {
icon.visibility = VISIBLE
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 0eea741..50bb9bc 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
@@ -115,11 +115,13 @@
fun hideFlyout_removedFromContainer() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ assertThat(flyoutController.hasFlyout()).isTrue()
assertThat(flyoutContainer.childCount).isEqualTo(1)
flyoutController.collapseFlyout {}
animatorTestRule.advanceTimeBy(300)
}
assertThat(flyoutContainer.childCount).isEqualTo(0)
+ assertThat(flyoutController.hasFlyout()).isFalse()
}
@Test
@@ -189,6 +191,61 @@
assertThat(flyoutCallbacks.flyoutClicked).isTrue()
}
+ @Test
+ fun updateFlyoutWhileExpanding() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ assertThat(flyoutController.hasFlyout()).isTrue()
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("message")
+ // advance the animation about halfway
+ animatorTestRule.advanceTimeBy(100)
+ }
+ assertThat(flyoutController.hasFlyout()).isTrue()
+
+ val newFlyoutMessage = flyoutMessage.copy(message = "new message")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ // set negative translation to verify that the top boundary extends as a result of
+ // updating while expanding
+ flyout.translationY = -50f
+ flyoutController.updateFlyoutWhileExpanding(newFlyoutMessage)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("new message")
+ }
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
+ }
+
+ @Test
+ fun updateFlyoutFullyExpanded() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ animatorTestRule.advanceTimeBy(300)
+ }
+ assertThat(flyoutController.hasFlyout()).isTrue()
+
+ val newFlyoutMessage = flyoutMessage.copy(message = "new message")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ // set negative translation to verify that the top boundary extends as a result of
+ // updating while fully expanded
+ flyout.translationY = -50f
+ flyoutController.updateFlyoutFullyExpanded(newFlyoutMessage) {}
+
+ // advance the timer so that the fade out animation plays
+ animatorTestRule.advanceTimeBy(250)
+ assertThat(flyout.alpha).isEqualTo(0)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("new message")
+
+ // advance the timer so that the fade in animation plays
+ animatorTestRule.advanceTimeBy(250)
+ assertThat(flyout.alpha).isEqualTo(1)
+ }
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
+ }
+
class FakeFlyoutCallbacks : FlyoutCallbacks {
var topBoundaryExtendedSpace = 0