Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt +70 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,17 @@ import com.android.systemui.contextualeducation.GestureType.BACK import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.touchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Test import org.junit.runner.RunWith Loading @@ -42,10 +47,15 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { private val keyboardRepository = kosmos.keyboardRepository private val touchpadRepository = kosmos.touchpadRepository private val repository = kosmos.contextualEducationRepository private val fakeClock = kosmos.fakeEduClock private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository private val initialDelayElapsedDuration = KeyboardTouchpadEduStatsInteractorImpl.initialDelayDuration + 1.seconds @Test fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() = testScope.runTest { setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) Loading @@ -58,6 +68,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() = testScope.runTest { setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) Loading @@ -70,6 +81,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() = testScope.runTest { setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) Loading @@ -82,6 +94,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() = testScope.runTest { setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) Loading @@ -99,4 +112,61 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { underTest.updateShortcutTriggerTime(BACK) assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant()) } @Test fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() = testScope.runTest { setUpForDeviceConnection() tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) fakeClock.offset(initialDelayElapsedDuration) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue + 1) } @Test fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() = testScope.runTest { setUpForDeviceConnection() tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) // No offset to the clock to simulate update before initial delay val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue) } @Test fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() = testScope.runTest { // No update to OOBE launch time to simulate no OOBE is launched yet setUpForDeviceConnection() val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue) } private suspend fun setUpForInitialDelayElapse() { tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, fakeClock.instant()) fakeClock.offset(initialDelayElapsedDuration) } private fun setUpForDeviceConnection() { touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(true) } @After fun clear() { testScope.launch { tutorialSchedulerRepository.clearDataStore() } } } packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt +31 −6 Original line number Diff line number Diff line Loading @@ -16,15 +16,23 @@ package com.android.systemui.education.domain.interactor import android.os.SystemProperties import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.ALL_APPS import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.dagger.ContextualEducationModule.EduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository 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 java.time.Clock import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.hours import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch Loading @@ -47,12 +55,24 @@ constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor, private val inputDeviceRepository: UserInputDeviceRepository, private val tutorialRepository: TutorialSchedulerRepository, @EduClock private val clock: Clock, ) : KeyboardTouchpadEduStatsInteractor { companion object { val initialDelayDuration: Duration get() = SystemProperties.getLong( "persist.contextual_edu.initial_delay_sec", /* defaultValue= */ 72.hours.inWholeSeconds ) .toDuration(DurationUnit.SECONDS) } override fun incrementSignalCount(gestureType: GestureType) { backgroundScope.launch { val targetDevice = getTargetDevice(gestureType) if (isTargetDeviceConnected(targetDevice)) { if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) { contextualEducationInteractor.incrementSignalCount(gestureType) } } Loading @@ -65,12 +85,10 @@ constructor( } private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean { if (deviceType == KEYBOARD) { return inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected } else if (deviceType == TOUCHPAD) { return inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected return when (deviceType) { KEYBOARD -> inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected TOUCHPAD -> inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected } return false } /** Loading @@ -83,4 +101,11 @@ constructor( ALL_APPS -> KEYBOARD else -> TOUCHPAD } private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean { val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false return clock .instant() .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds)) } } packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.education.domain.interactor import android.hardware.input.InputManager import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher Loading Loading @@ -57,6 +58,8 @@ var Kosmos.keyboardTouchpadEduStatsInteractor by keyboardRepository, touchpadRepository, userRepository ) ), tutorialSchedulerRepository, fakeEduClock ) } packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt +12 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.systemui.inputdevice.tutorial import android.content.applicationContext import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import org.mockito.kotlin.mock var Kosmos.inputDeviceTutorialLogger: InputDeviceTutorialLogger by Kosmos.Fixture { mock<InputDeviceTutorialLogger>() } var Kosmos.tutorialSchedulerRepository by Kosmos.Fixture { TutorialSchedulerRepository( applicationContext = applicationContext, testScope.backgroundScope, "KosmosTutorialSchedulerRepository" ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt +70 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,17 @@ import com.android.systemui.contextualeducation.GestureType.BACK import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.touchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Test import org.junit.runner.RunWith Loading @@ -42,10 +47,15 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { private val keyboardRepository = kosmos.keyboardRepository private val touchpadRepository = kosmos.touchpadRepository private val repository = kosmos.contextualEducationRepository private val fakeClock = kosmos.fakeEduClock private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository private val initialDelayElapsedDuration = KeyboardTouchpadEduStatsInteractorImpl.initialDelayDuration + 1.seconds @Test fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() = testScope.runTest { setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) Loading @@ -58,6 +68,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() = testScope.runTest { setUpForInitialDelayElapse() touchpadRepository.setIsAnyTouchpadConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) Loading @@ -70,6 +81,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() = testScope.runTest { setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(true) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) Loading @@ -82,6 +94,7 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { @Test fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() = testScope.runTest { setUpForInitialDelayElapse() keyboardRepository.setIsAnyKeyboardConnected(false) val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS)) Loading @@ -99,4 +112,61 @@ class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { underTest.updateShortcutTriggerTime(BACK) assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant()) } @Test fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() = testScope.runTest { setUpForDeviceConnection() tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) fakeClock.offset(initialDelayElapsedDuration) val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue + 1) } @Test fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() = testScope.runTest { setUpForDeviceConnection() tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) // No offset to the clock to simulate update before initial delay val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue) } @Test fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() = testScope.runTest { // No update to OOBE launch time to simulate no OOBE is launched yet setUpForDeviceConnection() val model by collectLastValue(repository.readGestureEduModelFlow(BACK)) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK) assertThat(model?.signalCount).isEqualTo(originalValue) } private suspend fun setUpForInitialDelayElapse() { tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant()) tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, fakeClock.instant()) fakeClock.offset(initialDelayElapsedDuration) } private fun setUpForDeviceConnection() { touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(true) } @After fun clear() { testScope.launch { tutorialSchedulerRepository.clearDataStore() } } }
packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt +31 −6 Original line number Diff line number Diff line Loading @@ -16,15 +16,23 @@ package com.android.systemui.education.domain.interactor import android.os.SystemProperties import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.ALL_APPS import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.dagger.ContextualEducationModule.EduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository 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 java.time.Clock import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.hours import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch Loading @@ -47,12 +55,24 @@ constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor, private val inputDeviceRepository: UserInputDeviceRepository, private val tutorialRepository: TutorialSchedulerRepository, @EduClock private val clock: Clock, ) : KeyboardTouchpadEduStatsInteractor { companion object { val initialDelayDuration: Duration get() = SystemProperties.getLong( "persist.contextual_edu.initial_delay_sec", /* defaultValue= */ 72.hours.inWholeSeconds ) .toDuration(DurationUnit.SECONDS) } override fun incrementSignalCount(gestureType: GestureType) { backgroundScope.launch { val targetDevice = getTargetDevice(gestureType) if (isTargetDeviceConnected(targetDevice)) { if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) { contextualEducationInteractor.incrementSignalCount(gestureType) } } Loading @@ -65,12 +85,10 @@ constructor( } private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean { if (deviceType == KEYBOARD) { return inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected } else if (deviceType == TOUCHPAD) { return inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected return when (deviceType) { KEYBOARD -> inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected TOUCHPAD -> inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected } return false } /** Loading @@ -83,4 +101,11 @@ constructor( ALL_APPS -> KEYBOARD else -> TOUCHPAD } private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean { val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false return clock .instant() .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds)) } }
packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.education.domain.interactor import android.hardware.input.InputManager import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher Loading Loading @@ -57,6 +58,8 @@ var Kosmos.keyboardTouchpadEduStatsInteractor by keyboardRepository, touchpadRepository, userRepository ) ), tutorialSchedulerRepository, fakeEduClock ) }
packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt +12 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.systemui.inputdevice.tutorial import android.content.applicationContext import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import org.mockito.kotlin.mock var Kosmos.inputDeviceTutorialLogger: InputDeviceTutorialLogger by Kosmos.Fixture { mock<InputDeviceTutorialLogger>() } var Kosmos.tutorialSchedulerRepository by Kosmos.Fixture { TutorialSchedulerRepository( applicationContext = applicationContext, testScope.backgroundScope, "KosmosTutorialSchedulerRepository" ) }