Merge "Handle taps on bubble bar flyout" into main
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/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/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
+ }
}
}