diff options
| author | 2024-10-18 16:33:47 +0100 | |
|---|---|---|
| committer | 2024-10-21 21:18:14 +0100 | |
| commit | 3b282f6af86be24700fbdd72d97ef4e16dac18bf (patch) | |
| tree | 025062686a1c84f7dbf4c8a0d6889b6c90662723 | |
| parent | 81c6cdc7803dc958bcf33d409ac3a0056e5dd7f8 (diff) | |
Introducing GestureUiState with extra animation info
We need more info in GestureState that is UI-specific so instead of adding it there, let's add extra layer between ActionState (state for touchpad + keyboard tutorial) and GestureState (formerly state for touchpad tutorial).
Because we need to do do different mapping for each touchpad gesture, introduced GestureFlowAdapter that wraps callback code into flow - this makes it less repetitive (and error prone) and we can subscribe it to it easily from Compose code.
Using conflatedCallbackFlow enforces using awaitClose() so also added new method to clear callback - it shouldn't be needed because we subscribe only when creating new GestureRecognizer but just to make it more explicit there's no memory leak.
Bug: 369817369
Test: small refactoring, tests pass
Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial
Change-Id: I0e9536af9a0fe475807721838383ad5cbe3912a3
9 files changed, 123 insertions, 15 deletions
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 e89a31f8fad3..618722a79a6f 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 @@ -27,7 +27,10 @@ import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenCon import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -48,7 +51,17 @@ fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni ), ) val recognizer = rememberBackGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_back_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable 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 7899f5b42a25..11e1ff49074a 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 @@ -17,6 +17,7 @@ package com.android.systemui.touchpad.tutorial.ui.composable 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 @@ -31,23 +32,49 @@ 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.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.EasterEggGestureMonitor import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer 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 com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler +import kotlinx.coroutines.flow.Flow -fun GestureState.toTutorialActionState(): TutorialActionState { +sealed interface GestureUiState { + data object NotStarted : GestureUiState + + data class Finished(@RawRes val successAnimation: Int) : GestureUiState + + data class InProgress( + val progress: Float = 0f, + val progressStartMark: String = "", + val progressEndMark: String = "", + ) : GestureUiState +} + +fun GestureState.toGestureUiState( + progressStartMark: String, + progressEndMark: String, + successAnimation: Int, +): GestureUiState { + return when (this) { + GestureState.NotStarted -> NotStarted + is GestureState.InProgress -> + GestureUiState.InProgress(this.progress, progressStartMark, progressEndMark) + is GestureState.Finished -> GestureUiState.Finished(successAnimation) + } +} + +fun GestureUiState.toTutorialActionState(): TutorialActionState { return when (this) { NotStarted -> TutorialActionState.NotStarted // progress is disabled for now as views are not ready to handle varying progress - is InProgress -> TutorialActionState.InProgress(0f) - Finished -> TutorialActionState.Finished + is GestureUiState.InProgress -> TutorialActionState.InProgress(progress = 0f) + is Finished -> TutorialActionState.Finished } } @@ -55,15 +82,13 @@ fun GestureState.toTutorialActionState(): TutorialActionState { fun GestureTutorialScreen( screenConfig: TutorialScreenConfig, gestureRecognizer: GestureRecognizer, + gestureUiStateFlow: Flow<GestureUiState>, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, ) { BackHandler(onBack = onBack) - var gestureState: GestureState by remember { mutableStateOf(NotStarted) } var easterEggTriggered by remember { mutableStateOf(false) } - LaunchedEffect(gestureRecognizer) { - gestureRecognizer.addGestureStateCallback { gestureState = it } - } + val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted) val easterEggMonitor = EasterEggGestureMonitor { easterEggTriggered = true } val gestureHandler = remember(gestureRecognizer) { TouchpadGestureHandler(gestureRecognizer, easterEggMonitor) } @@ -84,7 +109,7 @@ fun GestureTutorialScreen( @Composable private fun TouchpadGesturesHandlingBox( gestureHandler: TouchpadGestureHandler, - gestureState: GestureState, + gestureState: GestureUiState, easterEggTriggered: Boolean, resetEasterEggFlag: () -> Unit, modifier: Modifier = Modifier, @@ -110,7 +135,7 @@ private fun TouchpadGesturesHandlingBox( .pointerInteropFilter( onTouchEvent = { event -> // FINISHED is the final state so we don't need to process touches anymore - if (gestureState == Finished) { + if (gestureState is Finished) { false } else { gestureHandler.onMotionEvent(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 3ddf760b9704..05871ce91e39 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 @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -47,7 +50,17 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni ), ) val recognizer = rememberHomeGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_home_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable 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 30a21bf3b632..4fd16448e9f2 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 @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -47,7 +50,17 @@ fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () ), ) val recognizer = rememberRecentAppsGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_recent_apps_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt index 80f800390852..024048cbbb24 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt @@ -33,6 +33,10 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt new file mode 100644 index 000000000000..23e31b0a9efd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt @@ -0,0 +1,30 @@ +/* + * 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.gesture + +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +class GestureFlowAdapter(gestureRecognizer: GestureRecognizer) { + + val gestureStateAsFlow: Flow<GestureState> = conflatedCallbackFlow { + val callback: (GestureState) -> Unit = { trySend(it) } + gestureRecognizer.addGestureStateCallback(callback) + awaitClose { gestureRecognizer.clearGestureStateCallback() } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt index d146268304a6..68a2ef9528eb 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt @@ -22,6 +22,8 @@ import java.util.function.Consumer /** Based on passed [MotionEvent]s recognizes different states of gesture and notifies callback. */ interface GestureRecognizer : Consumer<MotionEvent> { fun addGestureStateCallback(callback: (GestureState) -> Unit) + + fun clearGestureStateCallback() } fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt index 2b84a4c50613..b804b9a0d4c7 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt @@ -29,6 +29,10 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt index 69b7c5edd750..7d484ee85b7c 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt @@ -38,6 +38,10 @@ class RecentAppsGestureRecognizer( gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) |