summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt31
5 files changed, 69 insertions, 133 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 4849e66d37d5..c51413a2cc78 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -26,7 +26,6 @@ import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.qsTileFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -42,7 +41,6 @@ class QSLongPressEffectTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val vibratorHelper = kosmos.vibratorHelper
- private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
private val effectDuration = 400
private val lowTickDuration = 12
@@ -63,7 +61,6 @@ class QSLongPressEffectTest : SysuiTestCase() {
vibratorHelper,
kosmos.keyguardInteractor,
)
- longPressEffect.qsTile = qsTile
}
@Test
@@ -94,10 +91,8 @@ class QSLongPressEffectTest : SysuiTestCase() {
// GIVEN an action down event occurs
longPressEffect.handleActionDown()
- // THEN the effect moves to the TIMEOUT_WAIT state and starts the wait
- val action by collectLastValue(longPressEffect.actionType)
+ // THEN the effect moves to the TIMEOUT_WAIT state
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
}
@Test
@@ -112,6 +107,20 @@ class QSLongPressEffectTest : SysuiTestCase() {
}
@Test
+ fun onActionUp_whileWaiting_performsClick() =
+ testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+ // GIVEN an action is being collected
+ val action by collectLastValue(longPressEffect.actionType)
+
+ // GIVEN an action up occurs
+ longPressEffect.handleActionUp()
+
+ // THEN the action to invoke is the click action and the effect does not start
+ assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK)
+ assertEffectDidNotStart()
+ }
+
+ @Test
fun onWaitComplete_whileWaiting_beginsEffect() =
testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
// GIVEN the pressed timeout is complete
@@ -212,10 +221,8 @@ class QSLongPressEffectTest : SysuiTestCase() {
// GIVEN that the animator was cancelled
longPressEffect.handleAnimationCancel()
- // THEN the state goes to the timeout wait and the wait is posted
- val action by collectLastValue(longPressEffect.actionType)
+ // THEN the state goes to the timeout wait
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
}
@Test
@@ -231,29 +238,6 @@ class QSLongPressEffectTest : SysuiTestCase() {
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
}
- @Test
- fun onTileClick_whileWaiting_withQSTile_clicks() =
- testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
- // GIVEN that a click was detected
- val couldClick = longPressEffect.onTileClick()
-
- // THEN the click is successful
- assertThat(couldClick).isTrue()
- }
-
- @Test
- fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
- testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
- // GIVEN that no QSTile has been set
- longPressEffect.qsTile = null
-
- // GIVEN that a click was detected
- val couldClick = longPressEffect.onTileClick()
-
- // THEN the click is not successful
- assertThat(couldClick).isFalse()
- }
-
private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index 30b958393b60..ea8d7d778851 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -19,9 +19,7 @@ package com.android.systemui.haptics.qs
import android.os.VibrationEffect
import android.view.View
import androidx.annotation.VisibleForTesting
-import com.android.systemui.animation.Expandable
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -53,10 +51,6 @@ constructor(
var state = State.IDLE
private set
- /** The QSTile and Expandable used to perform a long-click and click actions */
- var qsTile: QSTile? = null
- var expandable: Expandable? = null
-
/** Flow for view control and action */
private val _postedActionType = MutableStateFlow<ActionType?>(null)
val actionType: Flow<ActionType?> =
@@ -111,7 +105,6 @@ constructor(
when (state) {
State.IDLE -> {
setState(State.TIMEOUT_WAIT)
- _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
}
State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR
else -> {}
@@ -119,9 +112,16 @@ constructor(
}
fun handleActionUp() {
- if (state == State.RUNNING_FORWARD) {
- _postedActionType.value = ActionType.REVERSE_ANIMATOR
- setState(State.RUNNING_BACKWARDS)
+ when (state) {
+ State.TIMEOUT_WAIT -> {
+ _postedActionType.value = ActionType.CLICK
+ setState(State.IDLE)
+ }
+ State.RUNNING_FORWARD -> {
+ _postedActionType.value = ActionType.REVERSE_ANIMATOR
+ setState(State.RUNNING_BACKWARDS)
+ }
+ else -> {}
}
}
@@ -129,7 +129,6 @@ constructor(
when (state) {
State.TIMEOUT_WAIT -> {
setState(State.IDLE)
- clearActionType()
}
State.RUNNING_FORWARD -> {
_postedActionType.value = ActionType.REVERSE_ANIMATOR
@@ -146,23 +145,18 @@ constructor(
/** This function is called both when an animator completes or gets cancelled */
fun handleAnimationComplete() {
- when (state) {
- State.RUNNING_FORWARD -> {
- setState(State.IDLE)
- vibrate(snapEffect)
- _postedActionType.value = ActionType.LONG_PRESS
- }
- State.RUNNING_BACKWARDS -> {
- setState(State.IDLE)
- clearActionType()
- }
- else -> {}
+ if (state == State.RUNNING_FORWARD) {
+ vibrate(snapEffect)
+ _postedActionType.value = ActionType.LONG_PRESS
+ }
+ if (state != State.TIMEOUT_WAIT) {
+ // This will happen if the animator did not finish by being cancelled
+ setState(State.IDLE)
}
}
fun handleAnimationCancel() {
setState(State.TIMEOUT_WAIT)
- _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
}
fun handleTimeoutComplete() {
@@ -196,22 +190,9 @@ constructor(
effectDuration
)
setState(State.IDLE)
- clearActionType()
return true
}
- fun onTileClick(): Boolean {
- if (state == State.TIMEOUT_WAIT) {
- setState(State.IDLE)
- clearActionType()
- qsTile?.let {
- it.click(expandable)
- return true
- }
- }
- return false
- }
-
enum class State {
IDLE, /* The effect is idle waiting for touch input */
TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */
@@ -221,7 +202,7 @@ constructor(
/* A type of action to perform on the view depending on the effect's state and logic */
enum class ActionType {
- WAIT_TAP_TIMEOUT,
+ CLICK,
LONG_PRESS,
RESET_AND_LONG_PRESS,
START_ANIMATOR,
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
index 92a55ef0e74f..4875f481cce6 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
@@ -17,6 +17,8 @@
package com.android.systemui.haptics.qs
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.animation.doOnCancel
@@ -28,7 +30,6 @@ import com.android.app.tracing.coroutines.launch
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.qs.tileimpl.QSTileViewImpl
import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
object QSLongPressEffectViewBinder {
@@ -40,6 +41,9 @@ object QSLongPressEffectViewBinder {
): DisposableHandle? {
if (qsLongPressEffect == null) return null
+ // Set the touch listener as the long-press effect
+ setTouchListener(tile, qsLongPressEffect)
+
return tile.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
// Action to perform
@@ -48,18 +52,18 @@ object QSLongPressEffectViewBinder {
qsLongPressEffect.actionType.filterNotNull().collect { action ->
when (action) {
- QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT -> {
- delay(ViewConfiguration.getTapTimeout().toLong())
- qsLongPressEffect.handleTimeoutComplete()
+ QSLongPressEffect.ActionType.CLICK -> {
+ tile.performClick()
+ qsLongPressEffect.clearActionType()
}
QSLongPressEffect.ActionType.LONG_PRESS -> {
tile.prepareForLaunch()
- qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
+ tile.performLongClick()
qsLongPressEffect.clearActionType()
}
QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
tile.resetLongPressEffectProperties()
- qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
+ tile.performLongClick()
qsLongPressEffect.clearActionType()
}
QSLongPressEffect.ActionType.START_ANIMATOR -> {
@@ -102,4 +106,22 @@ object QSLongPressEffectViewBinder {
}
}
}
+
+ @SuppressLint("ClickableViewAccessibility")
+ private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) {
+ tile.setOnTouchListener { _, event ->
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ tile.postDelayed(
+ { longPressEffect?.handleTimeoutComplete() },
+ ViewConfiguration.getTapTimeout().toLong(),
+ )
+ longPressEffect?.handleActionDown()
+ }
+ MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp()
+ MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel()
+ }
+ true
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 1c4404db1fb1..c6dfdd5c137b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -19,7 +19,6 @@ package com.android.systemui.qs.tileimpl
import android.animation.ArgbEvaluator
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
-import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
@@ -38,7 +37,6 @@ import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
-import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
@@ -382,22 +380,15 @@ open class QSTileViewImpl @JvmOverloads constructor(
override fun init(tile: QSTile) {
val expandable = Expandable.fromView(this)
- if (quickSettingsVisualHapticsLongpress()) {
- isHapticFeedbackEnabled = false
- longPressEffect?.qsTile = tile
- longPressEffect?.expandable = expandable
- init(
- { _: View? -> longPressEffect?.onTileClick() },
- null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
- )
- } else {
- init(
+ init(
{ _: View? -> tile.click(expandable) },
{ _: View? ->
tile.longClick(expandable)
true
- },
- )
+ }
+ )
+ if (quickSettingsVisualHapticsLongpress()) {
+ isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect]
}
}
@@ -535,20 +526,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
return sb.toString()
}
- @SuppressLint("ClickableViewAccessibility")
- override fun onTouchEvent(event: MotionEvent?): Boolean {
- // let the View run the onTouch logic for click and long-click detection
- val result = super.onTouchEvent(event)
- if (longPressEffect != null) {
- when (event?.actionMasked) {
- MotionEvent.ACTION_DOWN -> longPressEffect.handleActionDown()
- MotionEvent.ACTION_UP -> longPressEffect.handleActionUp()
- MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel()
- }
- }
- return result
- }
-
// HANDLE STATE CHANGES RELATED METHODS
protected open fun handleStateChanged(state: QSTile.State) {
@@ -683,6 +660,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
// Long-press effects might have been enabled before but the new state does not
// handle a long-press. In this case, we go back to the behaviour of a regular tile
// and clean-up the resources
+ setOnTouchListener(null)
unbindLongPressEffect()
showRippleEffect = isClickable
initialLongPressProperties = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 196f654a205a..db11c3e89160 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -19,8 +19,6 @@ package com.android.systemui.qs.tileimpl
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -30,13 +28,11 @@ import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.qs.QSLongPressEffect
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.qsTileFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -540,30 +536,10 @@ class QSTileViewImplTest : SysuiTestCase() {
assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
}
- @Test
- @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
- fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
- val tile = kosmos.qsTileFactory.createTile("Test Tile")
- tileView.init(tile)
-
- assertThat(tileView.isTileAddedToLongPress).isTrue()
- assertThat(tileView.isExpandableAddedToLongPress).isTrue()
- }
-
- @Test
- @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
- fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
- val tile = kosmos.qsTileFactory.createTile("Test Tile")
- tileView.init(tile)
-
- assertThat(tileView.isTileAddedToLongPress).isFalse()
- assertThat(tileView.isExpandableAddedToLongPress).isFalse()
- }
-
class FakeTileView(
context: Context,
collapsed: Boolean,
- private val longPressEffect: QSLongPressEffect?,
+ longPressEffect: QSLongPressEffect?,
) : QSTileViewImpl(
ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
collapsed,
@@ -571,11 +547,6 @@ class QSTileViewImplTest : SysuiTestCase() {
) {
var constantLongPressEffectDuration = 500
- val isTileAddedToLongPress: Boolean
- get() = longPressEffect?.qsTile != null
- val isExpandableAddedToLongPress: Boolean
- get() = longPressEffect?.expandable != null
-
override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
fun changeState(state: QSTile.State) {
handleStateChanged(state)