Loading packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt +55 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.haptics.msdl.fakeMSDLPlayer import com.android.systemui.kosmos.testScope Loading @@ -39,9 +40,11 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.testKosmos import com.google.android.msdl.data.model.MSDLToken import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent Loading @@ -54,6 +57,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @EnableSceneContainer Loading Loading @@ -209,7 +213,55 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { verifyNoMoreInteractions(view) } private fun createTransitionState(from: SceneKey, to: ContentKey) = @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) @Test fun onRemoteUserInteraction_withValidSceneTransition_playsMSDLShadePullHaptics() = testScope.runTest { kosmos.disableDualShade() val isUserInteracting by collectLastValue(kosmos.shadeInteractor.isUserInteracting) // GIVEN a valid scene transition to play haptics that initiated remotely val validTransition = createTransitionState(from = Scenes.Gone, to = Scenes.Shade, byUser = false) sceneInteractor.onRemoteUserInputStarted("remote input") // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(validTransition)) runCurrent() assertThat(isUserInteracting).isFalse() // THEN the expected token plays without interaction properties assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWIPE_THRESHOLD_INDICATOR) assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) @Test fun onRemoteUserInteraction_withValidOverlayTransition_playsMSDLShadePullHaptics() = testScope.runTest { kosmos.enableDualShade() val isUserInteracting by collectLastValue(kosmos.shadeInteractor.isUserInteracting) // GIVEN a valid scene transition to play haptics that initiated remotely val validTransition = createTransitionState( from = Scenes.Gone, to = Overlays.NotificationsShade, byUser = false, ) sceneInteractor.onRemoteUserInputStarted("remote input") // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(validTransition)) runCurrent() assertThat(isUserInteracting).isFalse() // THEN the expected token plays without interaction properties assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWIPE_THRESHOLD_INDICATOR) assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } private fun createTransitionState(from: SceneKey, to: ContentKey, byUser: Boolean = true) = when (to) { is SceneKey -> ObservableTransitionState.Transition( Loading @@ -217,7 +269,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { toScene = to, currentScene = flowOf(from), progress = MutableStateFlow(0.2f), isInitiatedByUserInput = true, isInitiatedByUserInput = byUser, isUserInputOngoing = flowOf(true), ) is OverlayKey -> Loading @@ -228,7 +280,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { currentScene = from, currentOverlays = sceneInteractor.currentOverlays, progress = MutableStateFlow(0.2f), isInitiatedByUserInput = true, isInitiatedByUserInput = byUser, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), Loading packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModel.kt +7 −4 Original line number Diff line number Diff line Loading @@ -57,10 +57,13 @@ constructor( /** Should haptics be played by pulling down the shade */ private val isShadePullHapticsRequired: Flow<Boolean> = combine(shadeInteractor.isUserInteracting, sceneInteractor.transitionState) { interacting, transitionState -> interacting && transitionState.isValidForShadePullHaptics() combine( shadeInteractor.isUserInteracting, sceneInteractor.transitionState, sceneInteractor.isRemoteUserInteractionOngoing, ) { interacting, transitionState, remoteInteractionGoing -> val validInteraction = interacting || remoteInteractionGoing validInteraction && transitionState.isValidForShadePullHaptics() } .distinctUntilChanged() Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModelTest.kt +55 −3 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.haptics.msdl.fakeMSDLPlayer import com.android.systemui.kosmos.testScope Loading @@ -39,9 +40,11 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.testKosmos import com.google.android.msdl.data.model.MSDLToken import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent Loading @@ -54,6 +57,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @EnableSceneContainer Loading Loading @@ -209,7 +213,55 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { verifyNoMoreInteractions(view) } private fun createTransitionState(from: SceneKey, to: ContentKey) = @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) @Test fun onRemoteUserInteraction_withValidSceneTransition_playsMSDLShadePullHaptics() = testScope.runTest { kosmos.disableDualShade() val isUserInteracting by collectLastValue(kosmos.shadeInteractor.isUserInteracting) // GIVEN a valid scene transition to play haptics that initiated remotely val validTransition = createTransitionState(from = Scenes.Gone, to = Scenes.Shade, byUser = false) sceneInteractor.onRemoteUserInputStarted("remote input") // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(validTransition)) runCurrent() assertThat(isUserInteracting).isFalse() // THEN the expected token plays without interaction properties assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWIPE_THRESHOLD_INDICATOR) assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) @Test fun onRemoteUserInteraction_withValidOverlayTransition_playsMSDLShadePullHaptics() = testScope.runTest { kosmos.enableDualShade() val isUserInteracting by collectLastValue(kosmos.shadeInteractor.isUserInteracting) // GIVEN a valid scene transition to play haptics that initiated remotely val validTransition = createTransitionState( from = Scenes.Gone, to = Overlays.NotificationsShade, byUser = false, ) sceneInteractor.onRemoteUserInputStarted("remote input") // WHEN the transition occurs sceneInteractor.setTransitionState(MutableStateFlow(validTransition)) runCurrent() assertThat(isUserInteracting).isFalse() // THEN the expected token plays without interaction properties assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWIPE_THRESHOLD_INDICATOR) assertThat(msdlPlayer.latestPropertiesPlayed).isNull() } private fun createTransitionState(from: SceneKey, to: ContentKey, byUser: Boolean = true) = when (to) { is SceneKey -> ObservableTransitionState.Transition( Loading @@ -217,7 +269,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { toScene = to, currentScene = flowOf(from), progress = MutableStateFlow(0.2f), isInitiatedByUserInput = true, isInitiatedByUserInput = byUser, isUserInputOngoing = flowOf(true), ) is OverlayKey -> Loading @@ -228,7 +280,7 @@ class SceneContainerHapticsViewModelTest : SysuiTestCase() { currentScene = from, currentOverlays = sceneInteractor.currentOverlays, progress = MutableStateFlow(0.2f), isInitiatedByUserInput = true, isInitiatedByUserInput = byUser, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), Loading
packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerHapticsViewModel.kt +7 −4 Original line number Diff line number Diff line Loading @@ -57,10 +57,13 @@ constructor( /** Should haptics be played by pulling down the shade */ private val isShadePullHapticsRequired: Flow<Boolean> = combine(shadeInteractor.isUserInteracting, sceneInteractor.transitionState) { interacting, transitionState -> interacting && transitionState.isValidForShadePullHaptics() combine( shadeInteractor.isUserInteracting, sceneInteractor.transitionState, sceneInteractor.isRemoteUserInteractionOngoing, ) { interacting, transitionState, remoteInteractionGoing -> val validInteraction = interacting || remoteInteractionGoing validInteraction && transitionState.isValidForShadePullHaptics() } .distinctUntilChanged() Loading