Loading packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.model.SysUiState import com.android.systemui.settings.DisplayTracker import com.android.systemui.shared.system.QuickStepContract import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @SysUISingleton class TouchpadGesturesInteractor @Inject constructor( private val sysUiState: SysUiState, private val displayTracker: DisplayTracker, @Background private val backgroundScope: CoroutineScope ) { fun disableGestures() { setGesturesState(disabled = true) } fun enableGestures() { setGesturesState(disabled = false) } private fun setGesturesState(disabled: Boolean) { backgroundScope.launch { sysUiState .setFlag(QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED, disabled) .commitUpdate(displayTracker.defaultDisplayId) } } } packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt +14 −3 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.systemui.touchpad.tutorial.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject class TouchpadTutorialViewModel : ViewModel() { class TouchpadTutorialViewModel(private val gesturesInteractor: TouchpadGesturesInteractor) : ViewModel() { private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION) val screen: StateFlow<Screen> = _screen Loading @@ -31,11 +33,20 @@ class TouchpadTutorialViewModel : ViewModel() { _screen.value = screen } class Factory @Inject constructor() : ViewModelProvider.Factory { fun onOpened() { gesturesInteractor.disableGestures() } fun onClosed() { gesturesInteractor.enableGestures() } class Factory @Inject constructor(private val gesturesInteractor: TouchpadGesturesInteractor) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return TouchpadTutorialViewModel() as T return TouchpadTutorialViewModel(gesturesInteractor) as T } } } Loading packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +15 −6 Original line number Diff line number Diff line Loading @@ -20,10 +20,10 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.android.compose.theme.PlatformTheme Loading @@ -41,18 +41,27 @@ constructor( private val viewModelFactory: TouchpadTutorialViewModel.Factory, ) : ComponentActivity() { private val vm by viewModels<TouchpadTutorialViewModel>(factoryProducer = { viewModelFactory }) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) } setContent { PlatformTheme { TouchpadTutorialScreen(vm) { finish() } } } } override fun onResume() { super.onResume() vm.onOpened() } override fun onPause() { super.onPause() vm.onClosed() } } @Composable fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) { val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory) fun TouchpadTutorialScreen(vm: TouchpadTutorialViewModel, closeTutorial: () -> Unit) { val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) when (activeScreen) { TUTORIAL_SELECTION -> Loading packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModelTest.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * 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.model.sysUiState import com.android.systemui.settings.displayTracker import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class TouchpadTutorialViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = TestScope() private val sysUiState = kosmos.sysUiState private val viewModel = TouchpadTutorialViewModel( TouchpadGesturesInteractor(sysUiState, kosmos.displayTracker, testScope.backgroundScope) ) @Test fun sysUiStateFlag_disabledByDefault() = testScope.runTest { assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse() } @Test fun sysUiStateFlag_enabledAfterTutorialOpened() = testScope.runTest { viewModel.onOpened() assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isTrue() } @Test fun sysUiStateFlag_disabledAfterTutorialClosed() = testScope.runTest { viewModel.onOpened() viewModel.onClosed() assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse() } private fun TestScope.isFlagEnabled(@SystemUiStateFlags flag: Long): Boolean { // sysui state is changed on background scope so let's make sure it's executed runCurrent() return sysUiState.isFlagEnabled(flag) } } Loading
packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.model.SysUiState import com.android.systemui.settings.DisplayTracker import com.android.systemui.shared.system.QuickStepContract import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @SysUISingleton class TouchpadGesturesInteractor @Inject constructor( private val sysUiState: SysUiState, private val displayTracker: DisplayTracker, @Background private val backgroundScope: CoroutineScope ) { fun disableGestures() { setGesturesState(disabled = true) } fun enableGestures() { setGesturesState(disabled = false) } private fun setGesturesState(disabled: Boolean) { backgroundScope.launch { sysUiState .setFlag(QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED, disabled) .commitUpdate(displayTracker.defaultDisplayId) } } }
packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt +14 −3 Original line number Diff line number Diff line Loading @@ -18,11 +18,13 @@ package com.android.systemui.touchpad.tutorial.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject class TouchpadTutorialViewModel : ViewModel() { class TouchpadTutorialViewModel(private val gesturesInteractor: TouchpadGesturesInteractor) : ViewModel() { private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION) val screen: StateFlow<Screen> = _screen Loading @@ -31,11 +33,20 @@ class TouchpadTutorialViewModel : ViewModel() { _screen.value = screen } class Factory @Inject constructor() : ViewModelProvider.Factory { fun onOpened() { gesturesInteractor.disableGestures() } fun onClosed() { gesturesInteractor.enableGestures() } class Factory @Inject constructor(private val gesturesInteractor: TouchpadGesturesInteractor) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return TouchpadTutorialViewModel() as T return TouchpadTutorialViewModel(gesturesInteractor) as T } } } Loading
packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +15 −6 Original line number Diff line number Diff line Loading @@ -20,10 +20,10 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.android.compose.theme.PlatformTheme Loading @@ -41,18 +41,27 @@ constructor( private val viewModelFactory: TouchpadTutorialViewModel.Factory, ) : ComponentActivity() { private val vm by viewModels<TouchpadTutorialViewModel>(factoryProducer = { viewModelFactory }) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) } setContent { PlatformTheme { TouchpadTutorialScreen(vm) { finish() } } } } override fun onResume() { super.onResume() vm.onOpened() } override fun onPause() { super.onPause() vm.onClosed() } } @Composable fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) { val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory) fun TouchpadTutorialScreen(vm: TouchpadTutorialViewModel, closeTutorial: () -> Unit) { val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) when (activeScreen) { TUTORIAL_SELECTION -> Loading
packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModelTest.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * 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.model.sysUiState import com.android.systemui.settings.displayTracker import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class TouchpadTutorialViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = TestScope() private val sysUiState = kosmos.sysUiState private val viewModel = TouchpadTutorialViewModel( TouchpadGesturesInteractor(sysUiState, kosmos.displayTracker, testScope.backgroundScope) ) @Test fun sysUiStateFlag_disabledByDefault() = testScope.runTest { assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse() } @Test fun sysUiStateFlag_enabledAfterTutorialOpened() = testScope.runTest { viewModel.onOpened() assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isTrue() } @Test fun sysUiStateFlag_disabledAfterTutorialClosed() = testScope.runTest { viewModel.onOpened() viewModel.onClosed() assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse() } private fun TestScope.isFlagEnabled(@SystemUiStateFlags flag: Long): Boolean { // sysui state is changed on background scope so let's make sure it's executed runCurrent() return sysUiState.isFlagEnabled(flag) } }