Loading packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +11 −12 Original line number Diff line number Diff line Loading @@ -85,63 +85,62 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { @Test fun connectKeyboard_delayElapse_launchForKeyboard() = testScope.runTest { launchAndAssert(TutorialType.KEYBOARD) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.KEYBOARD) } @Test fun connectBothDevices_delayElapse_launchForBoth() = testScope.runTest { launchAndAssert(TutorialType.BOTH) keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.BOTH) } @Test fun connectBothDevice_delayNotElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) launchAndAssert(TutorialType.NONE) } @Test fun nothingConnect_delayElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(false) touchpadRepository.setIsAnyTouchpadConnected(false) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.NONE) } @Test fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() = testScope.runTest { launchAndAssert(TutorialType.BOTH) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.BOTH) } @Test fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(false) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.NONE) } private suspend fun launchAndAssert(expectedTutorial: TutorialType) = Loading packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt +16 −0 Original line number Diff line number Diff line Loading @@ -17,12 +17,14 @@ package com.android.systemui.inputdevice.tutorial.domain.interactor import android.os.SystemProperties import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.LAUNCH_DELAY import com.android.systemui.keyboard.data.repository.KeyboardRepository import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry Loading @@ -35,6 +37,7 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.toKotlinDuration import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow Loading Loading @@ -96,6 +99,9 @@ constructor( private suspend fun waitForDeviceConnection(deviceType: DeviceType) = isAnyDeviceConnected[deviceType]!!.filter { it }.first() // Only for testing notifications. This should behave independently from scheduling @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE) // Merging two flows ensures that tutorial is launched consecutively to avoid race condition val tutorials: Flow<TutorialType> = merge(touchpadScheduleFlow, keyboardScheduleFlow).map { Loading Loading @@ -146,6 +152,15 @@ constructor( pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}") pw.println(" launch time = ${repo.launchTime(TOUCHPAD)}") } "notify" -> { if (args.size != 2) help(pw) when (args[1]) { "keyboard" -> commandTutorials.value = TutorialType.KEYBOARD "touchpad" -> commandTutorials.value = TutorialType.TOUCHPAD "both" -> commandTutorials.value = TutorialType.BOTH else -> help(pw) } } else -> help(pw) } } Loading @@ -155,6 +170,7 @@ constructor( pw.println("Available commands:") pw.println(" clear") pw.println(" info") pw.println(" notify [keyboard|touchpad|both]") } } Loading packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt +7 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.core.app.NotificationCompat import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background Loading @@ -41,7 +42,7 @@ import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.flow.merge /** When the scheduler is due, show a notification to launch tutorial */ @SysUISingleton Loading @@ -56,7 +57,11 @@ constructor( ) { fun start() { backgroundScope.launch { tutorialSchedulerInteractor.tutorials.collect { showNotification(it) } merge( tutorialSchedulerInteractor.tutorials, tutorialSchedulerInteractor.commandTutorials, ) .collect { showNotification(it) } } } Loading packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +2 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.theme.PlatformTheme import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext Loading @@ -40,7 +41,6 @@ import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTUR import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE import java.util.Optional import javax.inject.Inject import com.android.app.tracing.coroutines.launchTraced as launch /** * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that Loading Loading @@ -90,6 +90,7 @@ constructor( setContent { PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) } } // TODO(b/376692701): Update launchTime when the activity is launched by Companion App if (savedInstanceState == null) { metricsLogger.logPeripheralTutorialLaunched( intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY), Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +11 −12 Original line number Diff line number Diff line Loading @@ -85,63 +85,62 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { @Test fun connectKeyboard_delayElapse_launchForKeyboard() = testScope.runTest { launchAndAssert(TutorialType.KEYBOARD) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.KEYBOARD) } @Test fun connectBothDevices_delayElapse_launchForBoth() = testScope.runTest { launchAndAssert(TutorialType.BOTH) keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.BOTH) } @Test fun connectBothDevice_delayNotElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) launchAndAssert(TutorialType.NONE) } @Test fun nothingConnect_delayElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(false) touchpadRepository.setIsAnyTouchpadConnected(false) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.NONE) } @Test fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() = testScope.runTest { launchAndAssert(TutorialType.BOTH) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.BOTH) } @Test fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() = testScope.runTest { launchAndAssert(TutorialType.NONE) keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(false) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.NONE) } private suspend fun launchAndAssert(expectedTutorial: TutorialType) = Loading
packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt +16 −0 Original line number Diff line number Diff line Loading @@ -17,12 +17,14 @@ package com.android.systemui.inputdevice.tutorial.domain.interactor import android.os.SystemProperties import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.LAUNCH_DELAY import com.android.systemui.keyboard.data.repository.KeyboardRepository import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry Loading @@ -35,6 +37,7 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.toKotlinDuration import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow Loading Loading @@ -96,6 +99,9 @@ constructor( private suspend fun waitForDeviceConnection(deviceType: DeviceType) = isAnyDeviceConnected[deviceType]!!.filter { it }.first() // Only for testing notifications. This should behave independently from scheduling @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE) // Merging two flows ensures that tutorial is launched consecutively to avoid race condition val tutorials: Flow<TutorialType> = merge(touchpadScheduleFlow, keyboardScheduleFlow).map { Loading Loading @@ -146,6 +152,15 @@ constructor( pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}") pw.println(" launch time = ${repo.launchTime(TOUCHPAD)}") } "notify" -> { if (args.size != 2) help(pw) when (args[1]) { "keyboard" -> commandTutorials.value = TutorialType.KEYBOARD "touchpad" -> commandTutorials.value = TutorialType.TOUCHPAD "both" -> commandTutorials.value = TutorialType.BOTH else -> help(pw) } } else -> help(pw) } } Loading @@ -155,6 +170,7 @@ constructor( pw.println("Available commands:") pw.println(" clear") pw.println(" info") pw.println(" notify [keyboard|touchpad|both]") } } Loading
packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt +7 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.core.app.NotificationCompat import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background Loading @@ -41,7 +42,7 @@ import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.flow.merge /** When the scheduler is due, show a notification to launch tutorial */ @SysUISingleton Loading @@ -56,7 +57,11 @@ constructor( ) { fun start() { backgroundScope.launch { tutorialSchedulerInteractor.tutorials.collect { showNotification(it) } merge( tutorialSchedulerInteractor.tutorials, tutorialSchedulerInteractor.commandTutorials, ) .collect { showNotification(it) } } } Loading
packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +2 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.theme.PlatformTheme import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext Loading @@ -40,7 +41,6 @@ import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTUR import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE import java.util.Optional import javax.inject.Inject import com.android.app.tracing.coroutines.launchTraced as launch /** * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that Loading Loading @@ -90,6 +90,7 @@ constructor( setContent { PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) } } // TODO(b/376692701): Update launchTime when the activity is launched by Companion App if (savedInstanceState == null) { metricsLogger.logPeripheralTutorialLaunched( intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY), Loading