Loading packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/data/repository/ActionCornerRepositoryTest.kt +130 −125 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.actioncorner.data.repository import android.graphics.Rect import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.view.Display.DEFAULT_DISPLAY import android.view.WindowInsets import android.view.WindowManager Loading @@ -41,8 +43,11 @@ import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.collectValues import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlin.time.Duration.Companion.milliseconds Loading @@ -63,11 +68,18 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.fakePointerRepository by Fixture { FakePointerDeviceRepository() } private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.underTest by Fixture { ActionCornerRepositoryImpl( cursorPositionRepository, kosmos.fakeDisplayWindowPropertiesRepository, kosmos.fakePointerRepository, ActionCornerSettingRepository( settingsRepository, testScope.backgroundScope, testDispatcher, ), kosmos.backgroundScope, ) } Loading @@ -84,22 +96,17 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun topLeftCursor_topLeftActionCornerEmitted() = kosmos.runTest { fun topLeftCursor_topLeftActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) addCursorPosition(display.topLeftCursorPos) assertThat(model) .isEqualTo( ActiveActionCorner( ActionCornerRegion.TOP_LEFT, display.topLeftCursorPos.displayId, ) ActiveActionCorner(ActionCornerRegion.TOP_LEFT, display.topLeftCursorPos.displayId) ) } @Test fun outOfBoundTopLeftCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundTopLeftCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topLeftCursorPos // Update x and y to make it just out of bound of action corner Loading @@ -114,20 +121,16 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun topRightCursor_topRightActionCornerEmitted() = kosmos.runTest { fun topRightCursor_topRightActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topRightCursorPos addCursorPosition(actionCornerPos) assertThat(model) .isEqualTo( ActiveActionCorner(ActionCornerRegion.TOP_RIGHT, actionCornerPos.displayId) ) .isEqualTo(ActiveActionCorner(ActionCornerRegion.TOP_RIGHT, actionCornerPos.displayId)) } @Test fun outOfBoundTopRightCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundTopRightCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topRightCursorPos addCursorPosition( Loading @@ -141,8 +144,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun bottomLeftCursor_bottomLeftActionCornerEmitted() = kosmos.runTest { fun bottomLeftCursor_bottomLeftActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomLeftCursorPos addCursorPosition(actionCornerPos) Loading @@ -153,8 +155,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun outOfBoundBottomLeftCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundBottomLeftCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomLeftCursorPos addCursorPosition( Loading @@ -168,8 +169,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun bottomRightCursor_bottomRightActionCornerEmitted() = kosmos.runTest { fun bottomRightCursor_bottomRightActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) Loading @@ -180,8 +180,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun outOfBoundBottomRightCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundBottomRightCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition( Loading @@ -196,12 +195,10 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @Test fun actionCornerCursor_moveOutOfBound_reEnterActionCorner_secondActiveActionCornerEmitted() = kosmos.runTest { setUpAndRunTest { // Filter out InactiveActionCorner for test readability val models by kosmos.collectValues( underTest.actionCornerState.filter { it != InactiveActionCorner } ) collectValues(underTest.actionCornerState.filter { it != InactiveActionCorner }) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) addCursorPosition(CursorPosition(x = 1000f, y = 1000f, actionCornerPos.displayId)) Loading @@ -213,33 +210,23 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun actionCornerCursor_moveInsideSameCorner_OneActionCornerEmitted() = kosmos.runTest { val models by kosmos.collectValues(underTest.actionCornerState.drop(1)) fun actionCornerCursor_moveInsideSameCorner_OneActionCornerEmitted() = setUpAndRunTest { val models by collectValues(underTest.actionCornerState.drop(1)) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) // Move within the same corner addCursorPosition( CursorPosition( actionCornerPos.x + 1, actionCornerPos.y + 1, actionCornerPos.displayId, ) CursorPosition(actionCornerPos.x + 1, actionCornerPos.y + 1, actionCornerPos.displayId) ) addCursorPosition( CursorPosition( actionCornerPos.x + 2, actionCornerPos.y + 2, actionCornerPos.displayId, ) CursorPosition(actionCornerPos.x + 2, actionCornerPos.y + 2, actionCornerPos.displayId) ) assertThat(models.size).isEqualTo(1) } @Test fun activeActionCorner_pointerDeviceDisconnected_inactiveActionCorner() = kosmos.runTest { fun activeActionCorner_pointerDeviceDisconnected_inactiveActionCorner() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos Loading @@ -252,7 +239,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @Test fun actionCornerState_remainsInactive_whenCursorMovesIntoActiveArea_butDebounceNotMet() = kosmos.runTest { setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos Loading @@ -262,11 +249,29 @@ class ActionCornerRepositoryTest : SysuiTestCase() { assertThat(model).isEqualTo(InactiveActionCorner) } @Test fun noActionConfigured_cursorMovesIntoActiveArea_remainInactiveActionCorner() = kosmos.runTest { // No action configured to corners by default val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) assertThat(model).isEqualTo(InactiveActionCorner) } private fun Kosmos.addCursorPosition(cursorPosition: CursorPosition) { cursorPositionRepository.addCursorPosition(cursorPosition) advanceTimeBy(DEBOUNCE_DELAY + 1.milliseconds) } private fun setUpAndRunTest(testBody: suspend Kosmos.() -> Unit) = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_HOME) testBody() } private fun createDisplayWindowProperties() = DisplayWindowProperties( DEFAULT_DISPLAY, Loading packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerRepository.kt +10 −5 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest Loading @@ -60,17 +61,21 @@ constructor( cursorRepository: MultiDisplayCursorPositionRepository, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, pointerDeviceRepository: PointerDeviceRepository, actionCornerSettingRepository: ActionCornerSettingRepository, @Background private val backgroundScope: CoroutineScope, ) : ActionCornerRepository { override val actionCornerState: StateFlow<ActionCornerState> = pointerDeviceRepository.isAnyPointerDeviceConnected .flatMapLatest { isConnected -> if (isConnected) { combine( pointerDeviceRepository.isAnyPointerDeviceConnected, actionCornerSettingRepository.isAnyActionConfigured, ) { isConnected, isAnyActionConfigured -> isConnected && isAnyActionConfigured } .flatMapLatest { shouldMonitorCursorPosition -> if (shouldMonitorCursorPosition) { cursorRepository.cursorPositions.map(::mapToActionCornerState) } else { // When not connected, emit an InactiveActionCorner state and then complete this // inner flow. flowOf(InactiveActionCorner) } } Loading packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepository.kt +14 −0 Original line number Diff line number Diff line Loading @@ -36,8 +36,11 @@ import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepo import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn Loading Loading @@ -65,6 +68,17 @@ constructor( val bottomRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_RIGHT_ACTION) val isAnyActionConfigured: Flow<Boolean> = combine( topLeftCornerAction, topRightCornerAction, bottomLeftCornerAction, bottomRightCornerAction, ) { topLeft, topRight, bottomLeft, bottomRight -> listOf(topLeft, topRight, bottomLeft, bottomRight).any { action -> action != NONE } } .distinctUntilChanged() private fun getCornerActionFlow(settingName: String): StateFlow<ActionType> { return settingsRepository .intSetting(name = settingName) Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/actioncorner/data/repository/ActionCornerRepositoryTest.kt +130 −125 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.actioncorner.data.repository import android.graphics.Rect import android.provider.Settings.Secure.ACTION_CORNER_ACTION_HOME import android.provider.Settings.Secure.ACTION_CORNER_TOP_LEFT_ACTION import android.view.Display.DEFAULT_DISPLAY import android.view.WindowInsets import android.view.WindowManager Loading @@ -41,8 +43,11 @@ import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.collectValues import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlin.time.Duration.Companion.milliseconds Loading @@ -63,11 +68,18 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.fakePointerRepository by Fixture { FakePointerDeviceRepository() } private val settingsRepository = kosmos.userAwareSecureSettingsRepository private val Kosmos.underTest by Fixture { ActionCornerRepositoryImpl( cursorPositionRepository, kosmos.fakeDisplayWindowPropertiesRepository, kosmos.fakePointerRepository, ActionCornerSettingRepository( settingsRepository, testScope.backgroundScope, testDispatcher, ), kosmos.backgroundScope, ) } Loading @@ -84,22 +96,17 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun topLeftCursor_topLeftActionCornerEmitted() = kosmos.runTest { fun topLeftCursor_topLeftActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) addCursorPosition(display.topLeftCursorPos) assertThat(model) .isEqualTo( ActiveActionCorner( ActionCornerRegion.TOP_LEFT, display.topLeftCursorPos.displayId, ) ActiveActionCorner(ActionCornerRegion.TOP_LEFT, display.topLeftCursorPos.displayId) ) } @Test fun outOfBoundTopLeftCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundTopLeftCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topLeftCursorPos // Update x and y to make it just out of bound of action corner Loading @@ -114,20 +121,16 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun topRightCursor_topRightActionCornerEmitted() = kosmos.runTest { fun topRightCursor_topRightActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topRightCursorPos addCursorPosition(actionCornerPos) assertThat(model) .isEqualTo( ActiveActionCorner(ActionCornerRegion.TOP_RIGHT, actionCornerPos.displayId) ) .isEqualTo(ActiveActionCorner(ActionCornerRegion.TOP_RIGHT, actionCornerPos.displayId)) } @Test fun outOfBoundTopRightCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundTopRightCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.topRightCursorPos addCursorPosition( Loading @@ -141,8 +144,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun bottomLeftCursor_bottomLeftActionCornerEmitted() = kosmos.runTest { fun bottomLeftCursor_bottomLeftActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomLeftCursorPos addCursorPosition(actionCornerPos) Loading @@ -153,8 +155,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun outOfBoundBottomLeftCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundBottomLeftCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomLeftCursorPos addCursorPosition( Loading @@ -168,8 +169,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun bottomRightCursor_bottomRightActionCornerEmitted() = kosmos.runTest { fun bottomRightCursor_bottomRightActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) Loading @@ -180,8 +180,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun outOfBoundBottomRightCursor_noActionCornerEmitted() = kosmos.runTest { fun outOfBoundBottomRightCursor_noActionCornerEmitted() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition( Loading @@ -196,12 +195,10 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @Test fun actionCornerCursor_moveOutOfBound_reEnterActionCorner_secondActiveActionCornerEmitted() = kosmos.runTest { setUpAndRunTest { // Filter out InactiveActionCorner for test readability val models by kosmos.collectValues( underTest.actionCornerState.filter { it != InactiveActionCorner } ) collectValues(underTest.actionCornerState.filter { it != InactiveActionCorner }) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) addCursorPosition(CursorPosition(x = 1000f, y = 1000f, actionCornerPos.displayId)) Loading @@ -213,33 +210,23 @@ class ActionCornerRepositoryTest : SysuiTestCase() { } @Test fun actionCornerCursor_moveInsideSameCorner_OneActionCornerEmitted() = kosmos.runTest { val models by kosmos.collectValues(underTest.actionCornerState.drop(1)) fun actionCornerCursor_moveInsideSameCorner_OneActionCornerEmitted() = setUpAndRunTest { val models by collectValues(underTest.actionCornerState.drop(1)) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) // Move within the same corner addCursorPosition( CursorPosition( actionCornerPos.x + 1, actionCornerPos.y + 1, actionCornerPos.displayId, ) CursorPosition(actionCornerPos.x + 1, actionCornerPos.y + 1, actionCornerPos.displayId) ) addCursorPosition( CursorPosition( actionCornerPos.x + 2, actionCornerPos.y + 2, actionCornerPos.displayId, ) CursorPosition(actionCornerPos.x + 2, actionCornerPos.y + 2, actionCornerPos.displayId) ) assertThat(models.size).isEqualTo(1) } @Test fun activeActionCorner_pointerDeviceDisconnected_inactiveActionCorner() = kosmos.runTest { fun activeActionCorner_pointerDeviceDisconnected_inactiveActionCorner() = setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos Loading @@ -252,7 +239,7 @@ class ActionCornerRepositoryTest : SysuiTestCase() { @Test fun actionCornerState_remainsInactive_whenCursorMovesIntoActiveArea_butDebounceNotMet() = kosmos.runTest { setUpAndRunTest { val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos Loading @@ -262,11 +249,29 @@ class ActionCornerRepositoryTest : SysuiTestCase() { assertThat(model).isEqualTo(InactiveActionCorner) } @Test fun noActionConfigured_cursorMovesIntoActiveArea_remainInactiveActionCorner() = kosmos.runTest { // No action configured to corners by default val model by collectLastValue(underTest.actionCornerState) val actionCornerPos = display.bottomRightCursorPos addCursorPosition(actionCornerPos) assertThat(model).isEqualTo(InactiveActionCorner) } private fun Kosmos.addCursorPosition(cursorPosition: CursorPosition) { cursorPositionRepository.addCursorPosition(cursorPosition) advanceTimeBy(DEBOUNCE_DELAY + 1.milliseconds) } private fun setUpAndRunTest(testBody: suspend Kosmos.() -> Unit) = kosmos.runTest { settingsRepository.setInt(ACTION_CORNER_TOP_LEFT_ACTION, ACTION_CORNER_ACTION_HOME) testBody() } private fun createDisplayWindowProperties() = DisplayWindowProperties( DEFAULT_DISPLAY, Loading
packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerRepository.kt +10 −5 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest Loading @@ -60,17 +61,21 @@ constructor( cursorRepository: MultiDisplayCursorPositionRepository, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, pointerDeviceRepository: PointerDeviceRepository, actionCornerSettingRepository: ActionCornerSettingRepository, @Background private val backgroundScope: CoroutineScope, ) : ActionCornerRepository { override val actionCornerState: StateFlow<ActionCornerState> = pointerDeviceRepository.isAnyPointerDeviceConnected .flatMapLatest { isConnected -> if (isConnected) { combine( pointerDeviceRepository.isAnyPointerDeviceConnected, actionCornerSettingRepository.isAnyActionConfigured, ) { isConnected, isAnyActionConfigured -> isConnected && isAnyActionConfigured } .flatMapLatest { shouldMonitorCursorPosition -> if (shouldMonitorCursorPosition) { cursorRepository.cursorPositions.map(::mapToActionCornerState) } else { // When not connected, emit an InactiveActionCorner state and then complete this // inner flow. flowOf(InactiveActionCorner) } } Loading
packages/SystemUI/src/com/android/systemui/actioncorner/data/repository/ActionCornerSettingRepository.kt +14 −0 Original line number Diff line number Diff line Loading @@ -36,8 +36,11 @@ import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepo import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn Loading Loading @@ -65,6 +68,17 @@ constructor( val bottomRightCornerAction: StateFlow<ActionType> = getCornerActionFlow(ACTION_CORNER_BOTTOM_RIGHT_ACTION) val isAnyActionConfigured: Flow<Boolean> = combine( topLeftCornerAction, topRightCornerAction, bottomLeftCornerAction, bottomRightCornerAction, ) { topLeft, topRight, bottomLeft, bottomRight -> listOf(topLeft, topRight, bottomLeft, bottomRight).any { action -> action != NONE } } .distinctUntilChanged() private fun getCornerActionFlow(settingName: String): StateFlow<ActionType> { return settingsRepository .intSetting(name = settingName) Loading