diff options
13 files changed, 261 insertions, 244 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt deleted file mode 100644 index 2ad1124d72d4..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.touchpad.tutorial.ui - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class StateTransitionsTest : SysuiTestCase() { - - companion object { - private const val START_MARKER = "startMarker" - private const val END_MARKER = "endMarker" - private const val SUCCESS_ANIMATION = 0 - } - - // needed to simulate caching last state as it's used to create new state - private var lastState: TutorialActionState = NotStarted - - private fun GestureState.toTutorialActionState(): TutorialActionState { - val newState = - this.toGestureUiState( - progressStartMarker = START_MARKER, - progressEndMarker = END_MARKER, - successAnimation = SUCCESS_ANIMATION, - ) - .toTutorialActionState(lastState) - lastState = newState - return lastState - } - - @Test - fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() { - val happyPath = - listOf( - GestureState.NotStarted, - GestureState.InProgress(0f), - GestureState.InProgress(0.5f), - GestureState.InProgress(1f), - GestureState.Finished, - ) - - val resultingStates = mutableListOf<TutorialActionState>() - happyPath.forEach { resultingStates.add(it.toTutorialActionState()) } - - assertThat(resultingStates) - .containsExactly( - NotStarted, - InProgress(0f, START_MARKER, END_MARKER), - InProgress(0.5f, START_MARKER, END_MARKER), - InProgress(1f, START_MARKER, END_MARKER), - Finished(SUCCESS_ANIMATION), - ) - .inOrder() - } - - @Test - fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() { - val errorPath = - listOf( - GestureState.NotStarted, - GestureState.InProgress(0f), - GestureState.Error, - GestureState.InProgress(0.5f), - GestureState.InProgress(1f), - GestureState.Finished, - ) - - val resultingStates = mutableListOf<TutorialActionState>() - errorPath.forEach { resultingStates.add(it.toTutorialActionState()) } - - assertThat(resultingStates) - .containsExactly( - NotStarted, - InProgress(0f, START_MARKER, END_MARKER), - Error, - InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)), - InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)), - Finished(SUCCESS_ANIMATION), - ) - .inOrder() - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt index 4aec88e8497b..d752046f4791 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources @@ -71,8 +71,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "gesture to L", - progressEndMarker = "end progress L", + startMarker = "gesture to L", + endMarker = "end progress L", ), ) } @@ -85,8 +85,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "gesture to R", - progressEndMarker = "end progress R", + startMarker = "gesture to R", + endMarker = "end progress R", ), ) } @@ -114,7 +114,7 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { kosmos.runTest { fun performBackGesture() = ThreeFingerGesture.swipeLeft().forEach { viewModel.handleEvent(it) } - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performBackGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -134,15 +134,21 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertProgressWhileMovingFingers(deltaX: Float, expected: GestureUiState) { + private fun Kosmos.assertProgressWhileMovingFingers( + deltaX: Float, + expected: TutorialActionState, + ) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, expected = expected, ) } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt index 65a995dcd043..7862fd32ca04 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity @@ -86,8 +86,8 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "drag with gesture", - progressEndMarker = "release playback realtime", + startMarker = "drag with gesture", + endMarker = "release playback realtime", ), ) } @@ -108,7 +108,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performHomeGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -121,7 +121,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performHomeGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -147,8 +147,11 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt index 1bc60b67095e..6180fa98b1cd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity @@ -89,8 +89,8 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "drag with gesture", - progressEndMarker = "onPause", + startMarker = "drag with gesture", + endMarker = "onPause", ), ) } @@ -111,7 +111,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performRecentAppsGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -124,7 +124,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performRecentAppsGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -150,8 +150,11 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt new file mode 100644 index 000000000000..c113dd9e1eff --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.touchpad.tutorial.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted +import com.android.systemui.kosmos.collectValues +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class TouchpadTutorialScreenViewModelTest : SysuiTestCase() { + + companion object { + private const val START_MARKER = "startMarker" + private const val END_MARKER = "endMarker" + private const val SUCCESS_ANIMATION = 0 + } + + private val kosmos = testKosmos() + private val animationProperties = + TutorialAnimationProperties( + progressStartMarker = START_MARKER, + progressEndMarker = END_MARKER, + successAnimation = SUCCESS_ANIMATION, + ) + + @Before + fun before() { + kosmos.useUnconfinedTestDispatcher() + } + + @Test + fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() = + kosmos.runTest { + val happyPath: Flow<Pair<GestureState, TutorialAnimationProperties>> = + listOf( + GestureState.NotStarted, + GestureState.InProgress(0f), + GestureState.InProgress(0.5f), + GestureState.InProgress(1f), + GestureState.Finished, + ) + .map { it to animationProperties } + .asFlow() + + val resultingStates by collectValues(happyPath.mapToTutorialState()) + + assertThat(resultingStates) + .containsExactly( + NotStarted, + InProgress(0f, START_MARKER, END_MARKER), + InProgress(0.5f, START_MARKER, END_MARKER), + InProgress(1f, START_MARKER, END_MARKER), + Finished(SUCCESS_ANIMATION), + ) + .inOrder() + } + + @Test + fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() = + kosmos.runTest { + val errorPath: Flow<Pair<GestureState, TutorialAnimationProperties>> = + listOf( + GestureState.NotStarted, + GestureState.InProgress(0f), + GestureState.Error, + GestureState.InProgress(0.5f), + GestureState.InProgress(1f), + GestureState.Finished, + ) + .map { it to animationProperties } + .asFlow() + + val resultingStates by collectValues(errorPath.mapToTutorialState()) + + assertThat(resultingStates) + .containsExactly( + NotStarted, + InProgress(0f, START_MARKER, END_MARKER), + Error, + InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)), + InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)), + Finished(SUCCESS_ANIMATION), + ) + .inOrder() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt index ae32b7a6175c..bce55cbdcc4a 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt @@ -50,7 +50,7 @@ fun BackGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt index 73c54af595d9..284e23e5a288 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt @@ -18,7 +18,6 @@ package com.android.systemui.touchpad.tutorial.ui.composable import android.view.MotionEvent import androidx.activity.compose.BackHandler -import androidx.annotation.RawRes import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box @@ -27,77 +26,21 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState import kotlinx.coroutines.flow.Flow -sealed interface GestureUiState { - data object NotStarted : GestureUiState - - data class Finished(@RawRes val successAnimation: Int) : GestureUiState - - data class InProgress( - val progress: Float = 0f, - val progressStartMarker: String, - val progressEndMarker: String, - ) : GestureUiState - - data object Error : GestureUiState -} - -fun GestureState.toGestureUiState( - progressStartMarker: String, - progressEndMarker: String, - successAnimation: Int, -): GestureUiState { - return when (this) { - GestureState.NotStarted -> NotStarted - is GestureState.InProgress -> - GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker) - is GestureState.Finished -> GestureUiState.Finished(successAnimation) - GestureState.Error -> GestureUiState.Error - } -} - -fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState { - return when (this) { - NotStarted -> TutorialActionState.NotStarted - is GestureUiState.InProgress -> { - val inProgress = - TutorialActionState.InProgress( - progress = progress, - startMarker = progressStartMarker, - endMarker = progressEndMarker, - ) - if ( - previousState is TutorialActionState.InProgressAfterError || - previousState is TutorialActionState.Error - ) { - return TutorialActionState.InProgressAfterError(inProgress) - } else { - return inProgress - } - } - is Finished -> TutorialActionState.Finished(successAnimation) - GestureUiState.Error -> TutorialActionState.Error - } -} - @Composable fun GestureTutorialScreen( screenConfig: TutorialScreenConfig, - gestureUiStateFlow: Flow<GestureUiState>, + tutorialStateFlow: Flow<TutorialActionState>, motionEventConsumer: (MotionEvent) -> Boolean, easterEggTriggeredFlow: Flow<Boolean>, onEasterEggFinished: () -> Unit, @@ -106,25 +49,21 @@ fun GestureTutorialScreen( ) { BackHandler(onBack = onBack) val easterEggTriggered by easterEggTriggeredFlow.collectAsStateWithLifecycle(false) - val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted) + val tutorialState by tutorialStateFlow.collectAsStateWithLifecycle(NotStarted) TouchpadGesturesHandlingBox( motionEventConsumer, - gestureState, + tutorialState, easterEggTriggered, onEasterEggFinished, ) { - var lastState: TutorialActionState by remember { - mutableStateOf(TutorialActionState.NotStarted) - } - lastState = gestureState.toTutorialActionState(lastState) - ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig) + ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig) } } @Composable private fun TouchpadGesturesHandlingBox( motionEventConsumer: (MotionEvent) -> Boolean, - gestureState: GestureUiState, + tutorialState: TutorialActionState, easterEggTriggered: Boolean, onEasterEggFinished: () -> Unit, modifier: Modifier = Modifier, @@ -150,7 +89,7 @@ private fun TouchpadGesturesHandlingBox( .pointerInteropFilter( onTouchEvent = { event -> // FINISHED is the final state so we don't need to process touches anymore - if (gestureState is Finished) { + if (tutorialState is TutorialActionState.Finished) { false } else { motionEventConsumer(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index 4f1f40dc4c05..4acdb6070200 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -49,7 +49,7 @@ fun HomeGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt index 6c9e26c4b7ea..8dd53a7fb815 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt @@ -50,7 +50,7 @@ fun RecentAppsGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt index 8e53669a7841..7a3d4d1ba88a 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt @@ -17,12 +17,12 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import com.android.systemui.util.kotlin.pairwiseBy import kotlinx.coroutines.flow.Flow @@ -30,21 +30,26 @@ import kotlinx.coroutines.flow.Flow class BackGestureScreenViewModel(val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.pairwiseBy(GestureState.NotStarted) { previous, current -> - toGestureUiState(current, previous) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .pairwiseBy(NotStarted) { previous, current -> + current to toAnimationProperties(current, previous) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) } - private fun toGestureUiState(current: GestureState, previous: GestureState): GestureUiState { + private fun toAnimationProperties( + current: GestureState, + previous: GestureState, + ): TutorialAnimationProperties { val (startMarker, endMarker) = if (current is InProgress && current.direction == GestureDirection.LEFT) { "gesture to L" to "end progress L" } else "gesture to R" to "end progress R" - return current.toGestureUiState( + return TutorialAnimationProperties( progressStartMarker = startMarker, progressEndMarker = endMarker, successAnimation = successAnimation(previous), diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt index 9d6f568fa1b1..c75d44f01e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt @@ -17,9 +17,8 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map class HomeGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.map { - it.toGestureUiState( - progressStartMarker = "drag with gesture", - progressEndMarker = "release playback realtime", - successAnimation = R.raw.trackpad_home_success, - ) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .map { + it to + TutorialAnimationProperties( + progressStartMarker = "drag with gesture", + progressEndMarker = "release playback realtime", + successAnimation = R.raw.trackpad_home_success, + ) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt index 97528583277f..9fab5f3641a4 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt @@ -17,9 +17,8 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map class RecentAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.map { - it.toGestureUiState( - progressStartMarker = "drag with gesture", - progressEndMarker = "onPause", - successAnimation = R.raw.trackpad_recent_apps_success, - ) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .map { + it to + TutorialAnimationProperties( + progressStartMarker = "drag with gesture", + progressEndMarker = "onPause", + successAnimation = R.raw.trackpad_recent_apps_success, + ) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt index 31e953d6643c..3b6e3c76cdeb 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt @@ -17,11 +17,62 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState +import androidx.annotation.RawRes +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow interface TouchpadTutorialScreenViewModel { - val gestureUiState: Flow<GestureUiState> + val tutorialState: Flow<TutorialActionState> fun handleEvent(event: MotionEvent): Boolean } + +data class TutorialAnimationProperties( + val progressStartMarker: String, + val progressEndMarker: String, + @RawRes val successAnimation: Int, +) + +fun Flow<Pair<GestureState, TutorialAnimationProperties>>.mapToTutorialState(): + Flow<TutorialActionState> { + return flow<TutorialActionState> { + var lastState: TutorialActionState = TutorialActionState.NotStarted + collect { (gestureState, animationProperties) -> + val newState = gestureState.toTutorialActionState(animationProperties, lastState) + lastState = newState + emit(newState) + } + } +} + +fun GestureState.toTutorialActionState( + properties: TutorialAnimationProperties, + previousState: TutorialActionState, +): TutorialActionState { + return when (this) { + NotStarted -> TutorialActionState.NotStarted + is InProgress -> { + val inProgress = + TutorialActionState.InProgress( + progress = progress, + startMarker = properties.progressStartMarker, + endMarker = properties.progressEndMarker, + ) + if ( + previousState is TutorialActionState.InProgressAfterError || + previousState is TutorialActionState.Error + ) { + TutorialActionState.InProgressAfterError(inProgress) + } else { + inProgress + } + } + is Finished -> TutorialActionState.Finished(properties.successAnimation) + GestureState.Error -> TutorialActionState.Error + } +} |