summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Michal Brzezinski <brzezinski@google.com> 2024-12-27 16:48:18 +0100
committer Michal Brzezinski <brzezinski@google.com> 2025-01-03 17:10:49 +0000
commit150e45970e8023747c0dca1df2db05121a6e1650 (patch)
tree39a9e8bbec8108d31d67938976d5c558f7e38179
parentd00d46e22dae09b398a452fb8ecc0321c34dcd1b (diff)
1/n refactoring: splitting ViewModels into separate smaller classes
Splitting ViewModels into smaller more cohesive classes with extracted common logic: - GestureRecognizerAdapter transforms callback based GestureRecognizer into Flow and substitutes GestureFlowAdapter - GestureRecognizerProvider which hides complexity of observing changes in resources and refreshing GestureRecognizer - TouchpadGestureResources extracts common code for accessing latest values for distance and velocity In that new class structure ViewModels responsibility is only to transform business state into Ui state that is to collect latest GestureState from GestureRecognizerAdapter and transform it into GestureUiState. Bug: 384509663 Test: all unit tests Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial Change-Id: I2da7cbeb530a1d4de91746d22b8126fd74e7154b
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggRecognizerProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt46
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt25
19 files changed, 432 insertions, 174 deletions
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 79c1f9fcf517..4aec88e8497b 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
@@ -16,12 +16,13 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
+import android.content.res.mockResources
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -34,18 +35,27 @@ import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finis
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
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class BackGestureScreenViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
+ private val resources = kosmos.mockResources
private val fakeConfigRepository = kosmos.fakeConfigurationRepository
- private val viewModel = BackGestureScreenViewModel(kosmos.configurationInteractor)
+ private val viewModel =
+ BackGestureScreenViewModel(
+ GestureRecognizerAdapter(
+ BackGestureRecognizerProvider(kosmos.touchpadGestureResources),
+ kosmos.inputDeviceTutorialLogger,
+ )
+ )
@Before
fun before() {
@@ -115,10 +125,12 @@ class BackGestureScreenViewModelTest : SysuiTestCase() {
}
private fun setThresholdResource(threshold: Float) {
- fakeConfigRepository.setDimensionPixelSize(
- R.dimen.touchpad_tutorial_gestures_distance_threshold,
- (threshold).toInt(),
- )
+ whenever(
+ resources.getDimensionPixelSize(
+ R.dimen.touchpad_tutorial_gestures_distance_threshold
+ )
+ )
+ .thenReturn(threshold.toInt())
fakeConfigRepository.onAnyConfigurationChange()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
index 4af374287c62..8bd796b5c851 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
@@ -20,6 +20,7 @@ import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -36,7 +37,13 @@ import org.junit.runner.RunWith
class EasterEggGestureViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val viewModel = EasterEggGestureViewModel()
+ private val viewModel =
+ EasterEggGestureViewModel(
+ GestureRecognizerAdapter(
+ EasterEggRecognizerProvider(),
+ kosmos.inputDeviceTutorialLogger,
+ )
+ )
@Before
fun before() {
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 4dfd01a91f17..65a995dcd043 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
@@ -22,7 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -37,6 +37,7 @@ import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Comp
import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -59,7 +60,12 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
private val resources = kosmos.mockResources
private val viewModel =
- HomeGestureScreenViewModel(kosmos.configurationInteractor, resources, fakeVelocityTracker)
+ HomeGestureScreenViewModel(
+ GestureRecognizerAdapter(
+ HomeGestureRecognizerProvider(kosmos.touchpadGestureResources, fakeVelocityTracker),
+ kosmos.inputDeviceTutorialLogger,
+ )
+ )
@Before
fun before() {
@@ -126,10 +132,12 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() {
}
private fun setDistanceThreshold(threshold: Float) {
- fakeConfigRepository.setDimensionPixelSize(
- R.dimen.touchpad_tutorial_gestures_distance_threshold,
- (threshold).toInt(),
- )
+ whenever(
+ resources.getDimensionPixelSize(
+ R.dimen.touchpad_tutorial_gestures_distance_threshold
+ )
+ )
+ .thenReturn(threshold.toInt())
fakeConfigRepository.onAnyConfigurationChange()
}
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 66bf778a754b..1bc60b67095e 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
@@ -22,7 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -37,6 +37,7 @@ import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Comp
import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -60,9 +61,13 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() {
private val viewModel =
RecentAppsGestureScreenViewModel(
- kosmos.configurationInteractor,
- resources,
- fakeVelocityTracker,
+ GestureRecognizerAdapter(
+ RecentAppsGestureRecognizerProvider(
+ kosmos.touchpadGestureResources,
+ fakeVelocityTracker,
+ ),
+ kosmos.inputDeviceTutorialLogger,
+ )
)
@Before
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
index a6c066500054..c43f31beb5bc 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
@@ -30,9 +30,15 @@ import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialS
import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.BackGestureRecognizerProvider
import com.android.systemui.touchpad.tutorial.ui.viewmodel.BackGestureScreenViewModel
import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggGestureViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggRecognizerProvider
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.GestureRecognizerAdapter
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureRecognizerProvider
import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureScreenViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureRecognizerProvider
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureScreenViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -53,14 +59,46 @@ interface TouchpadTutorialModule {
fun touchpadScreensProvider(
backGestureScreenViewModel: BackGestureScreenViewModel,
homeGestureScreenViewModel: HomeGestureScreenViewModel,
+ easterEggGestureViewModel: EasterEggGestureViewModel,
): TouchpadTutorialScreensProvider {
return ScreensProvider(
backGestureScreenViewModel,
homeGestureScreenViewModel,
- EasterEggGestureViewModel(),
+ easterEggGestureViewModel,
)
}
+ @Provides
+ fun recentAppsViewModel(
+ recognizerProvider: RecentAppsGestureRecognizerProvider,
+ adapterFactory: GestureRecognizerAdapter.Factory,
+ ): RecentAppsGestureScreenViewModel {
+ return RecentAppsGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+ }
+
+ @Provides
+ fun backViewModel(
+ recognizerProvider: BackGestureRecognizerProvider,
+ adapterFactory: GestureRecognizerAdapter.Factory,
+ ): BackGestureScreenViewModel {
+ return BackGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+ }
+
+ @Provides
+ fun homeViewModel(
+ recognizerProvider: HomeGestureRecognizerProvider,
+ adapterFactory: GestureRecognizerAdapter.Factory,
+ ): HomeGestureScreenViewModel {
+ return HomeGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+ }
+
+ @Provides
+ fun easterEggViewModel(
+ adapterFactory: GestureRecognizerAdapter.Factory
+ ): EasterEggGestureViewModel {
+ return EasterEggGestureViewModel(adapterFactory.create(EasterEggRecognizerProvider()))
+ }
+
@SysUISingleton
@Provides
fun touchpadGesturesInteractor(
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
index bddeb0b25ec2..b4b8ff0a3949 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
@@ -20,6 +20,7 @@ import android.view.InputDevice
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.BUTTON_PRIMARY
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.GestureRecognizerAdapter
object TouchpadEventsFilter {
@@ -42,3 +43,12 @@ fun GestureRecognizer.handleTouchpadMotionEvent(event: MotionEvent): Boolean {
false
}
}
+
+fun GestureRecognizerAdapter.handleTouchpadMotionEvent(event: MotionEvent): Boolean {
+ return if (TouchpadEventsFilter.isTouchpadAndNonClickEvent(event)) {
+ this.accept(event)
+ true
+ } else {
+ false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index cefe382a299c..3264300ed908 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -58,6 +58,7 @@ constructor(
private val backGestureViewModel: BackGestureScreenViewModel,
private val homeGestureViewModel: HomeGestureScreenViewModel,
private val recentAppsGestureViewModel: RecentAppsGestureScreenViewModel,
+ private val easterEggGestureViewModel: EasterEggGestureViewModel,
) : ComponentActivity() {
private val tutorialViewModel by
@@ -74,7 +75,7 @@ constructor(
backGestureViewModel,
homeGestureViewModel,
recentAppsGestureViewModel,
- EasterEggGestureViewModel(),
+ easterEggGestureViewModel,
closeTutorial = ::finishTutorial,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt
new file mode 100644
index 000000000000..b089882e9cf8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * 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 com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class BackGestureRecognizerProvider @Inject constructor(val resources: TouchpadGestureResources) :
+ GestureRecognizerProvider {
+
+ override val recognizer: Flow<GestureRecognizer> =
+ resources.distanceThreshold().map { distance ->
+ BackGestureRecognizer(gestureDistanceThresholdPx = distance)
+ }
+}
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 93e8d313edcf..8e53669a7841 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,47 +17,26 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
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.BackGestureRecognizer
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
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.handleTouchpadMotionEvent
import com.android.systemui.util.kotlin.pairwiseBy
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-class BackGestureScreenViewModel
-@Inject
-constructor(configurationInteractor: ConfigurationInteractor) : TouchpadTutorialScreenViewModel {
+class BackGestureScreenViewModel(val gestureRecognizer: GestureRecognizerAdapter) :
+ TouchpadTutorialScreenViewModel {
- private var recognizer: BackGestureRecognizer? = null
-
- private val distanceThreshold: Flow<Int> =
- configurationInteractor
- .dimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold)
- .distinctUntilChanged()
-
- @OptIn(ExperimentalCoroutinesApi::class)
override val gestureUiState: Flow<GestureUiState> =
- distanceThreshold
- .flatMapLatest {
- recognizer = BackGestureRecognizer(gestureDistanceThresholdPx = it)
- GestureFlowAdapter(recognizer!!).gestureStateAsFlow
- }
- .pairwiseBy(GestureState.NotStarted) { previous, current ->
- toGestureUiState(current, previous)
- }
+ gestureRecognizer.gestureState.pairwiseBy(GestureState.NotStarted) { previous, current ->
+ toGestureUiState(current, previous)
+ }
override fun handleEvent(event: MotionEvent): Boolean {
- return recognizer?.handleTouchpadMotionEvent(event) ?: false
+ return gestureRecognizer.handleTouchpadMotionEvent(event)
}
private fun toGestureUiState(current: GestureState, previous: GestureState): GestureUiState {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
index 69cdab6108ab..9ca456dff58a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
@@ -17,8 +17,6 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
import android.view.MotionEvent
-import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
import java.util.function.Consumer
@@ -29,14 +27,10 @@ import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
-class EasterEggGestureViewModel(
- private val gestureRecognizer: EasterEggGestureRecognizer = EasterEggGestureRecognizer()
-) : Consumer<MotionEvent> {
+class EasterEggGestureViewModel(val gestureRecognizer: GestureRecognizerAdapter) :
+ Consumer<MotionEvent> {
- private val gestureDone =
- GestureFlowAdapter(gestureRecognizer).gestureStateAsFlow.filter {
- it == GestureState.Finished
- }
+ private val gestureDone = gestureRecognizer.gestureState.filter { it == GestureState.Finished }
private val easterEggFinished = Channel<Unit>()
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/viewmodel/EasterEggRecognizerProvider.kt
index 23e31b0a9efd..c48ccb52ba0f 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggRecognizerProvider.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.touchpad.tutorial.ui.gesture
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import kotlinx.coroutines.channels.awaitClose
+import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
-class GestureFlowAdapter(gestureRecognizer: GestureRecognizer) {
+class EasterEggRecognizerProvider : GestureRecognizerProvider {
- val gestureStateAsFlow: Flow<GestureState> = conflatedCallbackFlow {
- val callback: (GestureState) -> Unit = { trySend(it) }
- gestureRecognizer.addGestureStateCallback(callback)
- awaitClose { gestureRecognizer.clearGestureStateCallback() }
- }
+ override val recognizer: Flow<GestureRecognizer> =
+ MutableStateFlow(EasterEggGestureRecognizer())
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
new file mode 100644
index 000000000000..8e7375fa923b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
@@ -0,0 +1,72 @@
+/*
+ * 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 android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.function.Consumer
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Adapter for [GestureRecognizer] exposing [GestureState] as Flow and ensuring that motion events
+ * are always handled by latest [GestureRecognizer].
+ */
+class GestureRecognizerAdapter
+@AssistedInject
+constructor(
+ @Assisted provider: GestureRecognizerProvider,
+ private val logger: InputDeviceTutorialLogger,
+) : Consumer<MotionEvent> {
+
+ private var gestureRecognizer: GestureRecognizer? = null
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ val gestureState: Flow<GestureState> =
+ provider.recognizer.flatMapLatest {
+ gestureRecognizer = it
+ gestureStateAsFlow(it)
+ }
+
+ override fun accept(event: MotionEvent) {
+ if (gestureRecognizer == null) {
+ logger.w("sending MotionEvent before gesture recognizer is initialized")
+ } else {
+ gestureRecognizer?.accept(event)
+ }
+ }
+
+ private fun gestureStateAsFlow(recognizer: GestureRecognizer): Flow<GestureState> =
+ conflatedCallbackFlow {
+ val callback: (GestureState) -> Unit = { trySend(it) }
+ recognizer.addGestureStateCallback(callback)
+ awaitClose { recognizer.clearGestureStateCallback() }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(provider: GestureRecognizerProvider): GestureRecognizerAdapter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt
new file mode 100644
index 000000000000..585bc0c425d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import kotlinx.coroutines.flow.Flow
+
+/** Observes state of the system and provides always up-to-date [GestureRecognizer] */
+interface GestureRecognizerProvider {
+ val recognizer: Flow<GestureRecognizer>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt
new file mode 100644
index 000000000000..6d818a6a0398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+class HomeGestureRecognizerProvider
+@Inject
+constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
+ GestureRecognizerProvider {
+
+ override val recognizer: Flow<GestureRecognizer> =
+ resources
+ .distanceThreshold()
+ .combine(
+ resources.velocityThreshold(R.dimen.touchpad_home_gesture_velocity_threshold),
+ { distance, velocity -> distance to velocity },
+ )
+ .map { (distance, velocity) ->
+ HomeGestureRecognizer(
+ gestureDistanceThresholdPx = distance,
+ velocityThresholdPxPerMs = velocity,
+ velocityTracker = velocityTracker,
+ )
+ }
+}
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 9a817d810bba..9d6f568fa1b1 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
@@ -16,70 +16,27 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
-import android.content.res.Resources
import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.dagger.qualifiers.Main
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.GestureFlowAdapter
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
-import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-class HomeGestureScreenViewModel
-@Inject
-constructor(
- val configurationInteractor: ConfigurationInteractor,
- @Main val resources: Resources,
- val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
-) : TouchpadTutorialScreenViewModel {
+class HomeGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
+ TouchpadTutorialScreenViewModel {
- private var recognizer: HomeGestureRecognizer? = null
-
- private val distanceThreshold: Flow<Int> =
- configurationInteractor
- .dimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold)
- .distinctUntilChanged()
-
- private val velocityThreshold: Flow<Float> =
- configurationInteractor.onAnyConfigurationChange
- .map { resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold) }
- .distinctUntilChanged()
-
- @OptIn(ExperimentalCoroutinesApi::class)
override val gestureUiState: Flow<GestureUiState> =
- distanceThreshold
- .combine(velocityThreshold, { distance, velocity -> distance to velocity })
- .flatMapLatest { (distance, velocity) ->
- recognizer =
- HomeGestureRecognizer(
- gestureDistanceThresholdPx = distance,
- velocityThresholdPxPerMs = velocity,
- velocityTracker = velocityTracker,
- )
- GestureFlowAdapter(recognizer!!).gestureStateAsFlow
- }
- .map { toGestureUiState(it) }
-
- private fun toGestureUiState(it: GestureState) =
- it.toGestureUiState(
- progressStartMarker = "drag with gesture",
- progressEndMarker = "release playback realtime",
- successAnimation = R.raw.trackpad_home_success,
- )
+ gestureRecognizer.gestureState.map {
+ it.toGestureUiState(
+ progressStartMarker = "drag with gesture",
+ progressEndMarker = "release playback realtime",
+ successAnimation = R.raw.trackpad_home_success,
+ )
+ }
override fun handleEvent(event: MotionEvent): Boolean {
- return recognizer?.handleTouchpadMotionEvent(event) ?: false
+ return gestureRecognizer.handleTouchpadMotionEvent(event)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt
new file mode 100644
index 000000000000..3e0b434036e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt
@@ -0,0 +1,49 @@
+/*
+ * 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 com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+class RecentAppsGestureRecognizerProvider
+@Inject
+constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
+ GestureRecognizerProvider {
+
+ override val recognizer: Flow<GestureRecognizer> =
+ resources
+ .distanceThreshold()
+ .combine(
+ resources.velocityThreshold(
+ R.dimen.touchpad_recent_apps_gesture_velocity_threshold
+ ),
+ { distance, velocity -> distance to velocity },
+ )
+ .map { (distance, velocity) ->
+ RecentAppsGestureRecognizer(
+ gestureDistanceThresholdPx = distance,
+ velocityThresholdPxPerMs = velocity,
+ velocityTracker = velocityTracker,
+ )
+ }
+}
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 8215078c346d..97528583277f 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
@@ -16,74 +16,27 @@
package com.android.systemui.touchpad.tutorial.ui.viewmodel
-import android.content.res.Resources
import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.dagger.qualifiers.Main
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.GestureFlowAdapter
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
-import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-class RecentAppsGestureScreenViewModel
-@Inject
-constructor(
- configurationInteractor: ConfigurationInteractor,
- @Main private val resources: Resources,
- private val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
-) : TouchpadTutorialScreenViewModel {
+class RecentAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
+ TouchpadTutorialScreenViewModel {
- private var recognizer: RecentAppsGestureRecognizer? = null
-
- private val distanceThreshold: Flow<Int> =
- configurationInteractor.onAnyConfigurationChange
- .map {
- resources.getDimensionPixelSize(
- R.dimen.touchpad_tutorial_gestures_distance_threshold
- )
- }
- .distinctUntilChanged()
-
- private val velocityThreshold: Flow<Float> =
- configurationInteractor.onAnyConfigurationChange
- .map { resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold) }
- .distinctUntilChanged()
-
- @OptIn(ExperimentalCoroutinesApi::class)
override val gestureUiState: Flow<GestureUiState> =
- distanceThreshold
- .combine(velocityThreshold, { distance, velocity -> distance to velocity })
- .flatMapLatest { (distance, velocity) ->
- recognizer =
- RecentAppsGestureRecognizer(
- gestureDistanceThresholdPx = distance,
- velocityThresholdPxPerMs = velocity,
- velocityTracker = velocityTracker,
- )
- GestureFlowAdapter(recognizer!!).gestureStateAsFlow
- }
- .map { toGestureUiState(it) }
-
- private fun toGestureUiState(it: GestureState) =
- it.toGestureUiState(
- progressStartMarker = "drag with gesture",
- progressEndMarker = "onPause",
- successAnimation = R.raw.trackpad_recent_apps_success,
- )
+ gestureRecognizer.gestureState.map {
+ it.toGestureUiState(
+ progressStartMarker = "drag with gesture",
+ progressEndMarker = "onPause",
+ successAnimation = R.raw.trackpad_recent_apps_success,
+ )
+ }
override fun handleEvent(event: MotionEvent): Boolean {
- return recognizer?.handleTouchpadMotionEvent(event) ?: false
+ return gestureRecognizer.handleTouchpadMotionEvent(event)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt
new file mode 100644
index 000000000000..3d99bd82d79e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt
@@ -0,0 +1,46 @@
+/*
+ * 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 android.content.res.Resources
+import androidx.annotation.DimenRes
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+class TouchpadGestureResources
+@Inject
+constructor(val configurationInteractor: ConfigurationInteractor, @Main val resources: Resources) {
+
+ fun distanceThreshold(): Flow<Int> =
+ configurationInteractor.onAnyConfigurationChange
+ .map {
+ resources.getDimensionPixelSize(
+ R.dimen.touchpad_tutorial_gestures_distance_threshold
+ )
+ }
+ .distinctUntilChanged()
+
+ fun velocityThreshold(@DimenRes resId: Int): Flow<Float> =
+ configurationInteractor.onAnyConfigurationChange
+ .map { resources.getDimension(resId) }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt
new file mode 100644
index 000000000000..d795941f96aa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.ui.gesture
+
+import android.content.res.mockResources
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.TouchpadGestureResources
+
+var Kosmos.touchpadGestureResources: TouchpadGestureResources by
+ Kosmos.Fixture { TouchpadGestureResources(configurationInteractor, mockResources) }