Loading packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt +16 −32 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -63,7 +61,6 @@ class QSLongPressEffectTest : SysuiTestCase() { vibratorHelper, kosmos.keyguardInteractor, ) longPressEffect.qsTile = qsTile } @Test Loading Loading @@ -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 Loading @@ -111,6 +106,20 @@ class QSLongPressEffectTest : SysuiTestCase() { assertEffectDidNotStart() } @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) { Loading Loading @@ -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 Loading @@ -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 { Loading packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +18 −37 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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?> = Loading Loading @@ -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 -> {} Loading @@ -119,17 +112,23 @@ constructor( } fun handleActionUp() { if (state == State.RUNNING_FORWARD) { 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 -> {} } } fun handleActionCancel() { when (state) { State.TIMEOUT_WAIT -> { setState(State.IDLE) clearActionType() } State.RUNNING_FORWARD -> { _postedActionType.value = ActionType.REVERSE_ANIMATOR Loading @@ -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) if (state == State.RUNNING_FORWARD) { vibrate(snapEffect) _postedActionType.value = ActionType.LONG_PRESS } State.RUNNING_BACKWARDS -> { if (state != State.TIMEOUT_WAIT) { // This will happen if the animator did not finish by being cancelled setState(State.IDLE) clearActionType() } else -> {} } } fun handleAnimationCancel() { setState(State.TIMEOUT_WAIT) _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT } fun handleTimeoutComplete() { Loading Loading @@ -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 */ Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt +28 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading @@ -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 Loading @@ -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 -> { Loading Loading @@ -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 } } } packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +6 −28 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -397,22 +395,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( { _: View? -> tile.click(expandable) }, { _: View? -> tile.longClick(expandable) true }, } ) if (quickSettingsVisualHapticsLongpress()) { isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect] } } Loading Loading @@ -550,20 +541,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) { Loading Loading @@ -698,6 +675,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 Loading packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +1 −30 Original line number Diff line number Diff line Loading @@ -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.TestableLooper import android.text.TextUtils Loading @@ -30,13 +28,11 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 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 Loading Loading @@ -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, Loading @@ -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) Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt +16 −32 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -63,7 +61,6 @@ class QSLongPressEffectTest : SysuiTestCase() { vibratorHelper, kosmos.keyguardInteractor, ) longPressEffect.qsTile = qsTile } @Test Loading Loading @@ -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 Loading @@ -111,6 +106,20 @@ class QSLongPressEffectTest : SysuiTestCase() { assertEffectDidNotStart() } @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) { Loading Loading @@ -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 Loading @@ -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 { Loading
packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +18 −37 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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?> = Loading Loading @@ -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 -> {} Loading @@ -119,17 +112,23 @@ constructor( } fun handleActionUp() { if (state == State.RUNNING_FORWARD) { 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 -> {} } } fun handleActionCancel() { when (state) { State.TIMEOUT_WAIT -> { setState(State.IDLE) clearActionType() } State.RUNNING_FORWARD -> { _postedActionType.value = ActionType.REVERSE_ANIMATOR Loading @@ -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) if (state == State.RUNNING_FORWARD) { vibrate(snapEffect) _postedActionType.value = ActionType.LONG_PRESS } State.RUNNING_BACKWARDS -> { if (state != State.TIMEOUT_WAIT) { // This will happen if the animator did not finish by being cancelled setState(State.IDLE) clearActionType() } else -> {} } } fun handleAnimationCancel() { setState(State.TIMEOUT_WAIT) _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT } fun handleTimeoutComplete() { Loading Loading @@ -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 */ Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt +28 −6 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading @@ -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 Loading @@ -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 -> { Loading Loading @@ -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 } } }
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +6 −28 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -397,22 +395,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( { _: View? -> tile.click(expandable) }, { _: View? -> tile.longClick(expandable) true }, } ) if (quickSettingsVisualHapticsLongpress()) { isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect] } } Loading Loading @@ -550,20 +541,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) { Loading Loading @@ -698,6 +675,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 Loading
packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +1 −30 Original line number Diff line number Diff line Loading @@ -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.TestableLooper import android.text.TextUtils Loading @@ -30,13 +28,11 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 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 Loading Loading @@ -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, Loading @@ -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) Loading