Update bubble bar swipe logic
After changes following swipe interactions are available for the bubble
bar:
1. stashed handle
- swipe up to show bubble bar, after swipe reaches unstash threshold, we
show the bubble bar
- swipe down to stash bubble bar in the same gesture (without lifting
finger), do this when swipe moves back below unstash threshold
- expand bubble bar on finger lifted when currently above unstash
threshold
2. collapsed bubble bar (used in home or overview)
- swipe up and release over unstash threshold to expand bubble bar
- bubble bar can't be swiped down to stash it
3. expanded bubble bar
- no swipe interactions available
Bug: 371229061
Flag: com.android.wm.shell.enable_bubble_bar
Test: BubbleBarSwipeControllerTest
Test: manually test:
- stashed bubble bar:
- swipe up and back down => bubble bar shown and stashed
- swipe up slightly and release => bubble bar not shown
- swipe up and release => bubble bar shown, expands on release
- collapsed bubble bar:
- swipe down => no action
- swipe up slightly => no action
- swipe up and release => bar expands on release
Change-Id: I42aa59dc288446603e06a3d02419be38cf17fa1f
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt
index bc562a6..2d3642b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeController.kt
@@ -21,15 +21,14 @@
import androidx.annotation.VisibleForTesting
import androidx.core.animation.doOnEnd
import androidx.dynamicanimation.animation.SpringForce
-import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.anim.SpringAnimationBuilder
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarThresholdUtils
-import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.StartState.COLLAPSED
-import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.StartState.EXPANDED
-import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.StartState.STASHED
-import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.StartState.UNKNOWN
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.BarState.COLLAPSED
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.BarState.EXPANDED
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.BarState.STASHED
+import com.android.launcher3.taskbar.bubbles.BubbleBarSwipeController.BarState.UNKNOWN
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.touch.OverScroll
@@ -46,11 +45,9 @@
private val animatedSwipeTranslation = AnimatedFloat(this::onSwipeUpdate)
private val unstashThreshold: Int
- private val expandThreshold: Int
private val maxOverscroll: Int
- private val stashThreshold: Int
- private var swipeState: SwipeState = SwipeState()
+ private var swipeState: SwipeState = SwipeState(startState = UNKNOWN)
constructor(tac: TaskbarActivityContext) : this(tac, DefaultDimensionProvider(tac))
@@ -58,9 +55,7 @@
constructor(context: Context, dimensionProvider: DimensionProvider) {
this.context = context
unstashThreshold = dimensionProvider.unstashThreshold
- expandThreshold = dimensionProvider.expandThreshold
maxOverscroll = dimensionProvider.maxOverscroll
- stashThreshold = dimensionProvider.stashThreshold
}
fun init(bubbleControllers: BubbleControllers) {
@@ -80,7 +75,7 @@
bubbleStashController.isBubbleBarVisible() -> COLLAPSED
else -> UNKNOWN
}
- swipeState = SwipeState(startState = startState)
+ swipeState = SwipeState(startState = startState, currentState = startState)
}
/** Update swipe distance to [dy] */
@@ -90,46 +85,25 @@
}
animatedSwipeTranslation.updateValue(dy)
- val prevState = swipeState
- // We can pass unstash threshold once per gesture, keep it true if it happened once
- val passedUnstashThreshold = isUnstash(dy) || prevState.passedUnstashThreshold
- // Expand happens at the end of the gesture, always keep the current value
- val passedExpandThreshold = isExpand(dy)
- // Stash happens at the end of the gesture, always keep the current value
- val passedStashThreshold = isStash(dy)
-
- if (
- passedUnstashThreshold != prevState.passedUnstashThreshold ||
- passedExpandThreshold != prevState.passedExpandThreshold ||
- passedStashThreshold != prevState.passedStashThreshold
- ) {
- swipeState =
- swipeState.copy(
- passedUnstashThreshold = passedUnstashThreshold,
- passedExpandThreshold = passedExpandThreshold,
- passedStashThreshold = passedStashThreshold,
- )
- }
-
- if (
- swipeState.startState == STASHED &&
- swipeState.passedUnstashThreshold &&
- !prevState.passedUnstashThreshold
- ) {
- bubbleStashController.showBubbleBar(expandBubbles = false)
+ swipeState.passedUnstash = isUnstash(dy)
+ // Tracking swipe gesture if we pass unstash threshold at least once during gesture
+ swipeState.isSwipe = swipeState.isSwipe || swipeState.passedUnstash
+ when {
+ canUnstash() && swipeState.passedUnstash -> {
+ swipeState.currentState = COLLAPSED
+ bubbleStashController.showBubbleBar(expandBubbles = false)
+ }
+ canStash() && !swipeState.passedUnstash -> {
+ swipeState.currentState = STASHED
+ bubbleStashController.stashBubbleBar()
+ }
}
}
/** Finish tracking swipe gesture. Animate views back to resting state */
fun finish() {
- when {
- swipeState.passedExpandThreshold &&
- swipeState.startState in setOf(STASHED, COLLAPSED) -> {
- bubbleStashController.showBubbleBar(expandBubbles = true)
- }
- swipeState.passedStashThreshold && swipeState.startState == COLLAPSED -> {
- bubbleStashController.stashBubbleBar()
- }
+ if (swipeState.passedUnstash && swipeState.startState in setOf(STASHED, COLLAPSED)) {
+ bubbleStashController.showBubbleBar(expandBubbles = true)
}
if (animatedSwipeTranslation.value == 0f) {
reset()
@@ -140,15 +114,21 @@
/** Returns `true` if we are tracking a swipe gesture */
fun isSwipeGesture(): Boolean {
- return swipeState.passedUnstashThreshold ||
- swipeState.passedExpandThreshold ||
- swipeState.passedStashThreshold
+ return swipeState.isSwipe
}
private fun canHandleSwipe(dy: Float): Boolean {
return when (swipeState.startState) {
- STASHED -> dy < 0 // stashed bar only handles swipe up
- COLLAPSED -> true // collapsed bar can be swiped in either direction
+ STASHED -> {
+ if (swipeState.currentState == COLLAPSED) {
+ // if we have unstashed the bar, allow swipe in both directions
+ true
+ } else {
+ // otherwise, only allow swipe up on stash handle
+ dy < 0
+ }
+ }
+ COLLAPSED -> dy < 0 // collapsed bar can only be swiped up
UNKNOWN,
EXPANDED -> false // expanded bar can't be swiped
}
@@ -158,12 +138,13 @@
return dy < -unstashThreshold
}
- private fun isExpand(dy: Float): Boolean {
- return dy < -expandThreshold
+ private fun canStash(): Boolean {
+ // Only allow stashing if we started from stashed state
+ return swipeState.startState == STASHED && swipeState.currentState == COLLAPSED
}
- private fun isStash(dy: Float): Boolean {
- return dy > stashThreshold
+ private fun canUnstash(): Boolean {
+ return swipeState.currentState == STASHED
}
private fun reset() {
@@ -175,7 +156,7 @@
}
}
springAnimation = null
- swipeState = SwipeState()
+ swipeState = SwipeState(startState = UNKNOWN)
}
private fun onSwipeUpdate(value: Float) {
@@ -197,13 +178,13 @@
}
internal data class SwipeState(
- val startState: StartState = UNKNOWN,
- val passedUnstashThreshold: Boolean = false,
- val passedExpandThreshold: Boolean = false,
- val passedStashThreshold: Boolean = false,
+ val startState: BarState,
+ var currentState: BarState = UNKNOWN,
+ var passedUnstash: Boolean = false,
+ var isSwipe: Boolean = false,
)
- internal enum class StartState {
+ internal enum class BarState {
UNKNOWN,
STASHED,
COLLAPSED,
@@ -214,17 +195,13 @@
@VisibleForTesting
interface DimensionProvider {
val unstashThreshold: Int
- val expandThreshold: Int
val maxOverscroll: Int
- val stashThreshold: Int
}
private class DefaultDimensionProvider(taskbarActivityContext: TaskbarActivityContext) :
DimensionProvider {
override val unstashThreshold: Int
- override val expandThreshold: Int
override val maxOverscroll: Int
- override val stashThreshold: Int
init {
val resources = taskbarActivityContext.resources
@@ -233,14 +210,7 @@
resources,
taskbarActivityContext.deviceProfile,
)
- // TODO(325673340): review threshold with ux
- expandThreshold =
- TaskbarThresholdUtils.getAppWindowThreshold(
- resources,
- taskbarActivityContext.deviceProfile,
- )
maxOverscroll = taskbarActivityContext.deviceProfile.heightPx - unstashThreshold
- stashThreshold = resources.getDimensionPixelSize(R.dimen.taskbar_to_nav_threshold)
}
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt
index 3b6952d..2e471b8 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarSwipeControllerTest.kt
@@ -37,6 +37,7 @@
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.never
+import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -45,15 +46,11 @@
companion object {
const val UNSTASH_THRESHOLD = 100
- const val EXPAND_THRESHOLD = 200
const val MAX_OVERSCROLL = 300
- const val STASH_THRESHOLD = 50
const val UP_BELOW_UNSTASH = -UNSTASH_THRESHOLD + 10f
const val UP_ABOVE_UNSTASH = -UNSTASH_THRESHOLD - 10f
- const val UP_ABOVE_EXPAND = -EXPAND_THRESHOLD - 10f
- const val DOWN_UNDER_STASH = STASH_THRESHOLD - 10f
- const val DOWN_OVER_STASH = STASH_THRESHOLD + 10f
+ const val DOWN = UNSTASH_THRESHOLD + 10f
}
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -80,14 +77,8 @@
override val unstashThreshold: Int
get() = UNSTASH_THRESHOLD
- override val expandThreshold: Int
- get() = EXPAND_THRESHOLD
-
override val maxOverscroll: Int
get() = MAX_OVERSCROLL
-
- override val stashThreshold: Int
- get() = STASH_THRESHOLD
}
bubbleBarSwipeController = BubbleBarSwipeController(context, dimensionProvider)
@@ -135,35 +126,11 @@
}
@Test
- fun swipeUp_stashedBar_aboveExpandThreshold_viewsHaveDampedTranslation() {
- setUpStashedBar()
- testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_EXPAND)
- }
-
- @Test
fun swipeUp_collapsedBar_aboveUnstashThreshold_viewsHaveDampedTranslation() {
setUpCollapsedBar()
testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_UNSTASH)
}
- @Test
- fun swipeUp_collapsedBar_aboveExpandThreshold_viewsHaveDampedTranslation() {
- setUpCollapsedBar()
- testViewsHaveDampedTranslationOnSwipe(UP_ABOVE_EXPAND)
- }
-
- @Test
- fun swipeDown_collapsedBar_belowStashThreshold_viewsHaveDampedTranslation() {
- setUpCollapsedBar()
- testViewsHaveDampedTranslationOnSwipe(DOWN_UNDER_STASH)
- }
-
- @Test
- fun swipeDown_collapsedBar_overStashThreshold_viewsHaveDampedTranslation() {
- setUpCollapsedBar()
- testViewsHaveDampedTranslationOnSwipe(DOWN_OVER_STASH)
- }
-
// endregion
// region Test that translation on views is reset on finish
@@ -203,29 +170,11 @@
}
@Test
- fun swipeUp_stashedBar_aboveExpandThreshold_animateTranslationToZeroOnFinish() {
- setUpStashedBar()
- testViewsTranslationResetOnFinish(UP_ABOVE_EXPAND)
- }
-
- @Test
fun swipeUp_collapsedBar_aboveUnstashThreshold_animateTranslationToZeroOnFinish() {
setUpCollapsedBar()
testViewsTranslationResetOnFinish(UP_ABOVE_UNSTASH)
}
- @Test
- fun swipeUp_collapsedBar_aboveExpandThreshold_animateTranslationToZeroOnFinish() {
- setUpCollapsedBar()
- testViewsTranslationResetOnFinish(UP_ABOVE_EXPAND)
- }
-
- @Test
- fun swipeDown_collapsedBar_aboveStashThreshold_animateTranslationToZeroOnFinish() {
- setUpCollapsedBar()
- testViewsTranslationResetOnFinish(DOWN_OVER_STASH)
- }
-
// endregion
// region Test swipe interactions on stashed bar
@@ -251,7 +200,7 @@
}
@Test
- fun swipeUp_stashedBar_aboveUnstashThreshold_unstashBubbleBar() {
+ fun swipeUp_stashedBar_overUnstashThreshold_unstashBubbleBar() {
setUpStashedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
@@ -271,50 +220,45 @@
}
@Test
- fun swipeUp_stashedBar_overUnstashThresholdMultipleTimes_unstashBubbleBarOnce() {
+ fun swipeUp_stashedBar_overUnstashThresholdMultipleTimes_unstashesMultipleTimes() {
setUpStashedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
- bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
}
verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ verify(bubbleStashController).stashBubbleBar()
+
+ getInstrumentation().runOnMainSync { bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH) }
+ verify(bubbleStashController, times(2)).showBubbleBar(expandBubbles = false)
}
@Test
- fun swipeUp_stashedBar_overExpandThreshold_doesNotExpandBeforeFinish() {
+ fun swipeUp_stashedBar_releaseOverUnstashThreshold_expandsBar() {
setUpStashedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
}
- verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ verify(bubbleStashController, never()).showBubbleBar(expandBubbles = true)
getInstrumentation().runOnMainSync { bubbleBarSwipeController.finish() }
verify(bubbleStashController).showBubbleBar(expandBubbles = true)
}
@Test
- fun swipeUp_stashedBar_overExpandThreshold_isSwipeGestureTrue() {
+ fun swipeUp_stashedBar_overUnstashReleaseBelowUnstash_doesNotExpandBar() {
setUpStashedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
- }
- assertThat(bubbleBarSwipeController.isSwipeGesture()).isTrue()
- }
-
- @Test
- fun swipeUp_stashedBar_overExpandThresholdAndBackDown_doesNotExpandAfterFinish() {
- setUpStashedBar()
- getInstrumentation().runOnMainSync {
- bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
}
verify(bubbleStashController).showBubbleBar(expandBubbles = false)
- getInstrumentation().runOnMainSync { bubbleBarSwipeController.finish() }
- verify(bubbleStashController).showBubbleBar(expandBubbles = false)
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ bubbleBarSwipeController.finish()
+ }
+ verify(bubbleStashController, never()).showBubbleBar(expandBubbles = true)
}
@Test
@@ -322,7 +266,7 @@
setUpStashedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(DOWN_OVER_STASH)
+ bubbleBarSwipeController.swipeTo(DOWN)
}
verify(bubbleStashedHandleViewController, never()).setTranslationYForSwipe(any())
verify(bubbleBarViewController, never()).setTranslationYForSwipe(any())
@@ -338,8 +282,8 @@
setUpExpandedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(UP_ABOVE_EXPAND)
- bubbleBarSwipeController.swipeTo(DOWN_OVER_STASH)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ bubbleBarSwipeController.swipeTo(DOWN)
bubbleBarSwipeController.finish()
}
verify(bubbleStashedHandleViewController, never()).setTranslationYForSwipe(any())
@@ -352,48 +296,71 @@
// region Test swipe interactions on collapsed bar
@Test
- fun swipeDown_collapsedBar_underStashThreshold_doesNotHideBar() {
+ fun swipeUp_collapsedBar_doesNotShowBarDuringDrag() {
setUpCollapsedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(DOWN_UNDER_STASH)
- bubbleBarSwipeController.finish()
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
}
- verify(bubbleStashController, never()).stashBubbleBar()
+ verify(bubbleStashController, never()).showBubbleBar(any())
}
@Test
- fun swipeDown_collapsedBar_overStashThreshold_doesNotHideBarBeforeFinish() {
+ fun swipeUp_collapsedBar_belowUnstashThreshold_isSwipeGestureFalse() {
setUpCollapsedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(DOWN_OVER_STASH)
- }
- verify(bubbleStashController, never()).stashBubbleBar()
- getInstrumentation().runOnMainSync { bubbleBarSwipeController.finish() }
- verify(bubbleStashController).stashBubbleBar()
- }
-
- @Test
- fun swipeDown_collapsedBar_underStashThreshold_isSwipeGestureFalse() {
- setUpCollapsedBar()
- getInstrumentation().runOnMainSync {
- bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(DOWN_UNDER_STASH)
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
}
assertThat(bubbleBarSwipeController.isSwipeGesture()).isFalse()
}
@Test
- fun swipeDown_collapsedBar_overStashThreshold_isSwipeGestureTrue() {
+ fun swipeUp_collapsedBar_overUnstashThreshold_isSwipeGestureTrue() {
setUpCollapsedBar()
getInstrumentation().runOnMainSync {
bubbleBarSwipeController.start()
- bubbleBarSwipeController.swipeTo(DOWN_OVER_STASH)
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
}
assertThat(bubbleBarSwipeController.isSwipeGesture()).isTrue()
}
+ @Test
+ fun swipeUp_collapsedBar_finishOverUnstashThreshold_expandsBar() {
+ setUpCollapsedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_ABOVE_UNSTASH)
+ bubbleBarSwipeController.finish()
+ }
+ verify(bubbleStashController).showBubbleBar(expandBubbles = true)
+ }
+
+ @Test
+ fun swipeUp_collapsedBar_finishBelowUnstashThreshold_doesNotExpandBar() {
+ setUpCollapsedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(UP_BELOW_UNSTASH)
+ bubbleBarSwipeController.finish()
+ }
+ verify(bubbleStashController, never()).showBubbleBar(any())
+ }
+
+ @Test
+ fun swipeDown_collapsedBar_swipeIgnored() {
+ setUpCollapsedBar()
+ getInstrumentation().runOnMainSync {
+ bubbleBarSwipeController.start()
+ bubbleBarSwipeController.swipeTo(DOWN)
+ }
+ verify(bubbleStashedHandleViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleBarViewController, never()).setTranslationYForSwipe(any())
+ verify(bubbleStashController, never()).showBubbleBar(any())
+ verify(bubbleStashController, never()).stashBubbleBar()
+ }
+
// endregion
private fun setUpStashedBar() {