Loading core/res/res/values/config.xml +5 −0 Original line number Diff line number Diff line Loading @@ -4184,6 +4184,11 @@ <!-- Whether device supports double tap to wake --> <bool name="config_supportDoubleTapWake">false</bool> <!-- Whether device supports double tap to sleep. This will allow the user to enable/disable double tap gestures in non-action areas in the lock screen and launcher workspace to go to sleep. --> <bool name="config_supportDoubleTapSleep">false</bool> <!-- The RadioAccessFamilies supported by the device. Empty is viewed as "all". Only used on devices which don't support RIL_REQUEST_GET_RADIO_CAPABILITY Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3142,6 +3142,7 @@ <java-symbol type="color" name="chooser_row_divider" /> <java-symbol type="layout" name="chooser_row_direct_share" /> <java-symbol type="bool" name="config_supportDoubleTapWake" /> <java-symbol type="bool" name="config_supportDoubleTapSleep" /> <java-symbol type="drawable" name="ic_perm_device_info" /> <java-symbol type="string" name="config_radio_access_family" /> <java-symbol type="string" name="notification_inbox_ellipsis" /> Loading packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt +216 −24 Original line number Diff line number Diff line Loading @@ -17,14 +17,12 @@ package com.android.systemui.common.ui.view import android.testing.TestableLooper import android.view.MotionEvent import android.view.ViewConfiguration import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Down import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Move import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Up import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat Loading @@ -33,18 +31,22 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit @Mock private lateinit var onSingleTapDetected: (Int, Int) -> Unit @Mock private lateinit var onDoubleTapDetected: () -> Unit private lateinit var underTest: TouchHandlingViewInteractionHandler Loading @@ -61,14 +63,17 @@ class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { underTest = TouchHandlingViewInteractionHandler( context = context, postDelayed = postDelayed, isAttachedToWindow = { isAttachedToWindow }, onLongPressDetected = onLongPressDetected, onSingleTapDetected = onSingleTapDetected, onDoubleTapDetected = onDoubleTapDetected, longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }, allowedTouchSlop = ViewConfiguration.getTouchSlop(), ) underTest.isLongPressHandlingEnabled = true underTest.isDoubleTapHandlingEnabled = true } @Test Loading @@ -76,63 +81,250 @@ class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { val downX = 123 val downY = 456 dispatchTouchEvents( Down(x = downX, y = downY), Move(distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain( 0L, 0L, MotionEvent.ACTION_MOVE, 123f + ViewConfiguration.getTouchSlop() - 0.1f, 456f, 0, ), ) delayedRunnable?.run() verify(onLongPressDetected).invoke(downX, downY) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun longPressButFeatureNotEnabled() = runTest { underTest.isLongPressHandlingEnabled = false dispatchTouchEvents(Down(x = 123, y = 456)) dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0)) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun longPressButViewNotAttached() = runTest { isAttachedToWindow = false dispatchTouchEvents(Down(x = 123, y = 456)) dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0)) delayedRunnable?.run() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun draggedTooFarToBeConsideredAlongPress() = runTest { dispatchTouchEvents( Down(x = 123, y = 456), Move(distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123F, 456F, 0), // Drag action within touch slop MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 123f, 456f, 0).apply { addBatch(0L, 123f + ViewConfiguration.getTouchSlop() + 0.1f, 456f, 0f, 0f, 0) }, ) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest { dispatchTouchEvents( Down(x = 123, y = 456), Up( distanceMoved = ViewConfiguration.getTouchSlop().toFloat(), gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L, MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain( 0L, ViewConfiguration.getLongPressTimeout() - 1L, MotionEvent.ACTION_UP, 123f, 456F, 0, ), ) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected).invoke(123, 456) } private fun dispatchTouchEvents(vararg models: MotionEventModel) { models.forEach { model -> underTest.onTouchEvent(model) } @Test fun doubleTap() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0), ) verify(onDoubleTapDetected).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun doubleTapButFeatureNotEnabled() = runTest { underTest.isDoubleTapHandlingEnabled = false val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0), ) verify(onDoubleTapDetected, never()).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun tapIntoLongPress() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain( secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, MotionEvent.ACTION_MOVE, 123f + ViewConfiguration.getTouchSlop() - 0.1f, 456f, 0, ), ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onSingleTapDetected).invoke(anyInt(), anyInt()) verify(onLongPressDetected).invoke(anyInt(), anyInt()) } @Test fun tapIntoDownHoldTooBrieflyToBeConsideredLongPress() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain( secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, MotionEvent.ACTION_UP, 123f, 456f, 0, ), ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun tapIntoDrag() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), // Drag event within touch slop MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_MOVE, 123f, 456f, 0) .apply { addBatch( secondTapTime, 123f + ViewConfiguration.getTouchSlop() + 0.1f, 456f, 0f, 0f, 0, ) }, ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected).invoke(anyInt(), anyInt()) } @Test fun doubleTapOutOfAllowableSlop() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L val scaledDoubleTapSlop = ViewConfiguration.get(context).scaledDoubleTapSlop dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f + scaledDoubleTapSlop + 0.1f, 456f + scaledDoubleTapSlop + 0.1f, 0, ), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f + scaledDoubleTapSlop + 0.1f, 456f + scaledDoubleTapSlop + 0.1f, 0, ), ) verify(onDoubleTapDetected, never()).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } private fun dispatchTouchEvents(vararg events: MotionEvent) { events.forEach { event -> underTest.onTouchEvent(event) } } } packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt +141 −6 Original line number Diff line number Diff line Loading @@ -18,11 +18,17 @@ package com.android.systemui.keyguard.domain.interactor import android.content.Intent import android.os.PowerManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings import android.view.accessibility.accessibilityManagerWrapper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.logging.uiEventLogger import com.android.systemui.Flags.FLAG_DOUBLE_TAP_TO_SLEEP import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor Loading @@ -39,6 +45,8 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy Loading @@ -46,14 +54,19 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { Loading @@ -61,17 +74,23 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { this.uiEventLogger = mock<UiEventLoggerFake>() } @get:Rule val setFlagsRule = SetFlagsRule() private lateinit var underTest: KeyguardTouchHandlingInteractor private val logger = kosmos.uiEventLogger private val testScope = kosmos.testScope private val keyguardRepository = kosmos.fakeKeyguardRepository private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val secureSettingsRepository = kosmos.userAwareSecureSettingsRepository @Mock private lateinit var powerManager: PowerManager @Before fun setUp() { MockitoAnnotations.initMocks(this) overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true) overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, true) whenever(kosmos.accessibilityManagerWrapper.getRecommendedTimeoutMillis(anyInt(), anyInt())) .thenAnswer { it.arguments[0] } Loading @@ -80,13 +99,13 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { @After fun tearDown() { mContext .getOrCreateTestableResources() .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled) val testableResource = mContext.getOrCreateTestableResources() testableResource.removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled) testableResource.removeOverride(com.android.internal.R.bool.config_supportDoubleTapSleep) } @Test fun isEnabled() = fun isLongPressEnabled() = testScope.runTest { val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) KeyguardState.values().forEach { keyguardState -> Loading @@ -101,7 +120,7 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { } @Test fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() = fun isLongPressEnabled_alwaysFalseWhenQuickSettingsAreVisible() = testScope.runTest { val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) KeyguardState.values().forEach { keyguardState -> Loading @@ -112,7 +131,7 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { } @Test fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() = fun isLongPressEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() = testScope.runTest { overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false) createUnderTest() Loading Loading @@ -294,6 +313,119 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { assertThat(isMenuVisible).isFalse() } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabled_userSettingEnabled_onlyTrueInLockScreenState() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) if (keyguardState == KeyguardState.LOCKSCREEN) { assertThat(isEnabled()).isTrue() } else { assertThat(isEnabled()).isFalse() } } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabled_userSettingDisabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @DisableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagDisabled_userSettingEnabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabledAndConfigDisabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, false) createUnderTest() val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_quickSettingsVisible_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun onDoubleClick_doubleTapEnabled() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled) runCurrent() underTest.onDoubleClick() assertThat(isEnabled).isTrue() verify(powerManager).goToSleep(anyLong()) } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun onDoubleClick_doubleTapDisabled() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false) val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled) runCurrent() underTest.onDoubleClick() assertThat(isEnabled).isFalse() verify(powerManager, never()).goToSleep(anyLong()) } } private suspend fun createUnderTest(isRevampedWppFeatureEnabled: Boolean = true) { // This needs to be re-created for each test outside of kosmos since the flag values are // read during initialization to set up flows. Maybe there is a better way to handle that. Loading @@ -309,6 +441,9 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { accessibilityManager = kosmos.accessibilityManagerWrapper, pulsingGestureListener = kosmos.pulsingGestureListener, faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor, secureSettingsRepository = secureSettingsRepository, powerManager = powerManager, systemClock = kosmos.fakeSystemClock, ) setUpState() } Loading packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt +18 −37 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/res/res/values/config.xml +5 −0 Original line number Diff line number Diff line Loading @@ -4184,6 +4184,11 @@ <!-- Whether device supports double tap to wake --> <bool name="config_supportDoubleTapWake">false</bool> <!-- Whether device supports double tap to sleep. This will allow the user to enable/disable double tap gestures in non-action areas in the lock screen and launcher workspace to go to sleep. --> <bool name="config_supportDoubleTapSleep">false</bool> <!-- The RadioAccessFamilies supported by the device. Empty is viewed as "all". Only used on devices which don't support RIL_REQUEST_GET_RADIO_CAPABILITY Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3142,6 +3142,7 @@ <java-symbol type="color" name="chooser_row_divider" /> <java-symbol type="layout" name="chooser_row_direct_share" /> <java-symbol type="bool" name="config_supportDoubleTapWake" /> <java-symbol type="bool" name="config_supportDoubleTapSleep" /> <java-symbol type="drawable" name="ic_perm_device_info" /> <java-symbol type="string" name="config_radio_access_family" /> <java-symbol type="string" name="notification_inbox_ellipsis" /> Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt +216 −24 Original line number Diff line number Diff line Loading @@ -17,14 +17,12 @@ package com.android.systemui.common.ui.view import android.testing.TestableLooper import android.view.MotionEvent import android.view.ViewConfiguration import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Down import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Move import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Up import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat Loading @@ -33,18 +31,22 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit @Mock private lateinit var onSingleTapDetected: (Int, Int) -> Unit @Mock private lateinit var onDoubleTapDetected: () -> Unit private lateinit var underTest: TouchHandlingViewInteractionHandler Loading @@ -61,14 +63,17 @@ class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { underTest = TouchHandlingViewInteractionHandler( context = context, postDelayed = postDelayed, isAttachedToWindow = { isAttachedToWindow }, onLongPressDetected = onLongPressDetected, onSingleTapDetected = onSingleTapDetected, onDoubleTapDetected = onDoubleTapDetected, longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }, allowedTouchSlop = ViewConfiguration.getTouchSlop(), ) underTest.isLongPressHandlingEnabled = true underTest.isDoubleTapHandlingEnabled = true } @Test Loading @@ -76,63 +81,250 @@ class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() { val downX = 123 val downY = 456 dispatchTouchEvents( Down(x = downX, y = downY), Move(distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain( 0L, 0L, MotionEvent.ACTION_MOVE, 123f + ViewConfiguration.getTouchSlop() - 0.1f, 456f, 0, ), ) delayedRunnable?.run() verify(onLongPressDetected).invoke(downX, downY) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun longPressButFeatureNotEnabled() = runTest { underTest.isLongPressHandlingEnabled = false dispatchTouchEvents(Down(x = 123, y = 456)) dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0)) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun longPressButViewNotAttached() = runTest { isAttachedToWindow = false dispatchTouchEvents(Down(x = 123, y = 456)) dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0)) delayedRunnable?.run() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun draggedTooFarToBeConsideredAlongPress() = runTest { dispatchTouchEvents( Down(x = 123, y = 456), Move(distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123F, 456F, 0), // Drag action within touch slop MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 123f, 456f, 0).apply { addBatch(0L, 123f + ViewConfiguration.getTouchSlop() + 0.1f, 456f, 0f, 0f, 0) }, ) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onSingleTapDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt()) } @Test fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest { dispatchTouchEvents( Down(x = 123, y = 456), Up( distanceMoved = ViewConfiguration.getTouchSlop().toFloat(), gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L, MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain( 0L, ViewConfiguration.getLongPressTimeout() - 1L, MotionEvent.ACTION_UP, 123f, 456F, 0, ), ) assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(any(), any()) verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected).invoke(123, 456) } private fun dispatchTouchEvents(vararg models: MotionEventModel) { models.forEach { model -> underTest.onTouchEvent(model) } @Test fun doubleTap() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0), ) verify(onDoubleTapDetected).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun doubleTapButFeatureNotEnabled() = runTest { underTest.isDoubleTapHandlingEnabled = false val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0), ) verify(onDoubleTapDetected, never()).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun tapIntoLongPress() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain( secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, MotionEvent.ACTION_MOVE, 123f + ViewConfiguration.getTouchSlop() - 0.1f, 456f, 0, ), ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onSingleTapDetected).invoke(anyInt(), anyInt()) verify(onLongPressDetected).invoke(anyInt(), anyInt()) } @Test fun tapIntoDownHoldTooBrieflyToBeConsideredLongPress() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), MotionEvent.obtain( secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L, MotionEvent.ACTION_UP, 123f, 456f, 0, ), ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } @Test fun tapIntoDrag() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f, 456f, 0, ), // Drag event within touch slop MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_MOVE, 123f, 456f, 0) .apply { addBatch( secondTapTime, 123f + ViewConfiguration.getTouchSlop() + 0.1f, 456f, 0f, 0f, 0, ) }, ) delayedRunnable?.run() verify(onDoubleTapDetected, never()).invoke() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected).invoke(anyInt(), anyInt()) } @Test fun doubleTapOutOfAllowableSlop() = runTest { val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L val scaledDoubleTapSlop = ViewConfiguration.get(context).scaledDoubleTapSlop dispatchTouchEvents( MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0), MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_DOWN, 123f + scaledDoubleTapSlop + 0.1f, 456f + scaledDoubleTapSlop + 0.1f, 0, ), MotionEvent.obtain( secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f + scaledDoubleTapSlop + 0.1f, 456f + scaledDoubleTapSlop + 0.1f, 0, ), ) verify(onDoubleTapDetected, never()).invoke() assertThat(delayedRunnable).isNull() verify(onLongPressDetected, never()).invoke(anyInt(), anyInt()) verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt()) } private fun dispatchTouchEvents(vararg events: MotionEvent) { events.forEach { event -> underTest.onTouchEvent(event) } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt +141 −6 Original line number Diff line number Diff line Loading @@ -18,11 +18,17 @@ package com.android.systemui.keyguard.domain.interactor import android.content.Intent import android.os.PowerManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings import android.view.accessibility.accessibilityManagerWrapper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.logging.uiEventLogger import com.android.systemui.Flags.FLAG_DOUBLE_TAP_TO_SLEEP import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor Loading @@ -39,6 +45,8 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy Loading @@ -46,14 +54,19 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { Loading @@ -61,17 +74,23 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { this.uiEventLogger = mock<UiEventLoggerFake>() } @get:Rule val setFlagsRule = SetFlagsRule() private lateinit var underTest: KeyguardTouchHandlingInteractor private val logger = kosmos.uiEventLogger private val testScope = kosmos.testScope private val keyguardRepository = kosmos.fakeKeyguardRepository private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val secureSettingsRepository = kosmos.userAwareSecureSettingsRepository @Mock private lateinit var powerManager: PowerManager @Before fun setUp() { MockitoAnnotations.initMocks(this) overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true) overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, true) whenever(kosmos.accessibilityManagerWrapper.getRecommendedTimeoutMillis(anyInt(), anyInt())) .thenAnswer { it.arguments[0] } Loading @@ -80,13 +99,13 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { @After fun tearDown() { mContext .getOrCreateTestableResources() .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled) val testableResource = mContext.getOrCreateTestableResources() testableResource.removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled) testableResource.removeOverride(com.android.internal.R.bool.config_supportDoubleTapSleep) } @Test fun isEnabled() = fun isLongPressEnabled() = testScope.runTest { val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) KeyguardState.values().forEach { keyguardState -> Loading @@ -101,7 +120,7 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { } @Test fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() = fun isLongPressEnabled_alwaysFalseWhenQuickSettingsAreVisible() = testScope.runTest { val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) KeyguardState.values().forEach { keyguardState -> Loading @@ -112,7 +131,7 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { } @Test fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() = fun isLongPressEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() = testScope.runTest { overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false) createUnderTest() Loading Loading @@ -294,6 +313,119 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { assertThat(isMenuVisible).isFalse() } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabled_userSettingEnabled_onlyTrueInLockScreenState() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) if (keyguardState == KeyguardState.LOCKSCREEN) { assertThat(isEnabled()).isTrue() } else { assertThat(isEnabled()).isFalse() } } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabled_userSettingDisabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @DisableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagDisabled_userSettingEnabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_flagEnabledAndConfigDisabled_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, false) createUnderTest() val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun isDoubleTapEnabled_quickSettingsVisible_alwaysFalse() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled) KeyguardState.entries.forEach { keyguardState -> setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true) assertThat(isEnabled()).isFalse() } } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun onDoubleClick_doubleTapEnabled() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true) val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled) runCurrent() underTest.onDoubleClick() assertThat(isEnabled).isTrue() verify(powerManager).goToSleep(anyLong()) } } @Test @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP) fun onDoubleClick_doubleTapDisabled() { testScope.runTest { secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false) val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled) runCurrent() underTest.onDoubleClick() assertThat(isEnabled).isFalse() verify(powerManager, never()).goToSleep(anyLong()) } } private suspend fun createUnderTest(isRevampedWppFeatureEnabled: Boolean = true) { // This needs to be re-created for each test outside of kosmos since the flag values are // read during initialization to set up flows. Maybe there is a better way to handle that. Loading @@ -309,6 +441,9 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() { accessibilityManager = kosmos.accessibilityManagerWrapper, pulsingGestureListener = kosmos.pulsingGestureListener, faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor, secureSettingsRepository = secureSettingsRepository, powerManager = powerManager, systemClock = kosmos.fakeSystemClock, ) setUpState() } Loading
packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt +18 −37 File changed.Preview size limit exceeded, changes collapsed. Show changes