Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt +3 −1 Original line number Diff line number Diff line Loading @@ -85,7 +85,9 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { GestureEduModel( signalCount = 2, educationShownCount = 1, lastShortcutTriggeredTime = kosmos.fakeEduClock.instant() lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(), lastEducationTime = kosmos.fakeEduClock.instant(), usageSessionStartTime = kosmos.fakeEduClock.instant(), ) underTest.updateGestureEduModel(BACK) { newModel } val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +49 −3 Original line number Diff line number Diff line Loading @@ -22,9 +22,15 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.BACK import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.model.GestureEduModel import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.education.shared.model.EducationUiType import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading @@ -37,6 +43,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val contextualEduInteractor = kosmos.contextualEducationInteractor private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor private val eduClock = kosmos.fakeEduClock @Before fun setup() { Loading @@ -46,11 +53,31 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { @Test fun newEducationInfoOnMaxSignalCountReached() = testScope.runTest { tryTriggeringEducation(BACK) triggerMaxEducationSignals(BACK) val model by collectLastValue(underTest.educationTriggered) assertThat(model?.gestureType).isEqualTo(BACK) } @Test fun newEducationToastOn1stEducation() = testScope.runTest { val model by collectLastValue(underTest.educationTriggered) triggerMaxEducationSignals(BACK) assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast) } @Test @kotlinx.coroutines.ExperimentalCoroutinesApi fun newEducationNotificationOn2ndEducation() = testScope.runTest { val model by collectLastValue(underTest.educationTriggered) triggerMaxEducationSignals(BACK) // runCurrent() to trigger 1st education runCurrent() triggerMaxEducationSignals(BACK) assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification) } @Test fun noEducationInfoBeforeMaxSignalCountReached() = testScope.runTest { Loading @@ -64,11 +91,30 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { testScope.runTest { val model by collectLastValue(underTest.educationTriggered) contextualEduInteractor.updateShortcutTriggerTime(BACK) tryTriggeringEducation(BACK) triggerMaxEducationSignals(BACK) assertThat(model).isNull() } private suspend fun tryTriggeringEducation(gestureType: GestureType) { @Test fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() = testScope.runTest { val model by collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK)) contextualEduInteractor.incrementSignalCount(BACK) eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds)) val secondSignalReceivedTime = eduClock.instant() contextualEduInteractor.incrementSignalCount(BACK) assertThat(model) .isEqualTo( GestureEduModel( signalCount = 1, usageSessionStartTime = secondSignalReceivedTime ) ) } private suspend fun triggerMaxEducationSignals(gestureType: GestureType) { // Increment max number of signal to try triggering education for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) { contextualEduInteractor.incrementSignalCount(gestureType) Loading packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -26,4 +26,6 @@ data class GestureEduModel( val signalCount: Int = 0, val educationShownCount: Int = 0, val lastShortcutTriggeredTime: Instant? = null, val usageSessionStartTime: Instant? = null, val lastEducationTime: Instant? = null, ) packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt +28 −2 Original line number Diff line number Diff line Loading @@ -73,6 +73,8 @@ constructor( const val SIGNAL_COUNT_SUFFIX = "_SIGNAL_COUNT" const val NUMBER_OF_EDU_SHOWN_SUFFIX = "_NUMBER_OF_EDU_SHOWN" const val LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX = "_LAST_SHORTCUT_TRIGGERED_TIME" const val USAGE_SESSION_START_TIME_SUFFIX = "_USAGE_SESSION_START_TIME" const val LAST_EDUCATION_TIME_SUFFIX = "_LAST_EDUCATION_TIME" const val DATASTORE_DIR = "education/USER%s_ContextualEducation" } Loading Loading @@ -113,6 +115,14 @@ constructor( preferences[getLastShortcutTriggeredTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, usageSessionStartTime = preferences[getUsageSessionStartTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, lastEducationTime = preferences[getLastEducationTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, ) } Loading @@ -125,11 +135,21 @@ constructor( val updatedModel = transform(currentModel) preferences[getSignalCountKey(gestureType)] = updatedModel.signalCount preferences[getEducationShownCountKey(gestureType)] = updatedModel.educationShownCount updateTimeByInstant( setInstant( preferences, updatedModel.lastShortcutTriggeredTime, getLastShortcutTriggeredTimeKey(gestureType) ) setInstant( preferences, updatedModel.usageSessionStartTime, getUsageSessionStartTimeKey(gestureType) ) setInstant( preferences, updatedModel.lastEducationTime, getLastEducationTimeKey(gestureType) ) } } Loading @@ -142,7 +162,13 @@ constructor( private fun getLastShortcutTriggeredTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX) private fun updateTimeByInstant( private fun getUsageSessionStartTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + USAGE_SESSION_START_TIME_SUFFIX) private fun getLastEducationTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + LAST_EDUCATION_TIME_SUFFIX) private fun setInstant( preferences: MutablePreferences, instant: Instant?, key: Preferences.Key<Long> Loading packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt +25 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,13 @@ constructor( } suspend fun incrementSignalCount(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy(signalCount = it.signalCount + 1) } repository.updateGestureEduModel(gestureType) { it.copy( signalCount = it.signalCount + 1, usageSessionStartTime = if (it.signalCount == 0) clock.instant() else it.usageSessionStartTime ) } } suspend fun updateShortcutTriggerTime(gestureType: GestureType) { Loading @@ -76,4 +82,22 @@ constructor( it.copy(lastShortcutTriggeredTime = clock.instant()) } } suspend fun updateOnEduTriggered(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy( // Reset signal counter and usageSessionStartTime after edu triggered signalCount = 0, lastEducationTime = clock.instant(), educationShownCount = it.educationShownCount + 1, usageSessionStartTime = null ) } } suspend fun startNewUsageSession(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy(usageSessionStartTime = clock.instant(), signalCount = 1) } } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt +3 −1 Original line number Diff line number Diff line Loading @@ -85,7 +85,9 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { GestureEduModel( signalCount = 2, educationShownCount = 1, lastShortcutTriggeredTime = kosmos.fakeEduClock.instant() lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(), lastEducationTime = kosmos.fakeEduClock.instant(), usageSessionStartTime = kosmos.fakeEduClock.instant(), ) underTest.updateGestureEduModel(BACK) { newModel } val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +49 −3 Original line number Diff line number Diff line Loading @@ -22,9 +22,15 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.BACK import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.model.GestureEduModel import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.education.shared.model.EducationUiType import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading @@ -37,6 +43,7 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val contextualEduInteractor = kosmos.contextualEducationInteractor private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor private val eduClock = kosmos.fakeEduClock @Before fun setup() { Loading @@ -46,11 +53,31 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { @Test fun newEducationInfoOnMaxSignalCountReached() = testScope.runTest { tryTriggeringEducation(BACK) triggerMaxEducationSignals(BACK) val model by collectLastValue(underTest.educationTriggered) assertThat(model?.gestureType).isEqualTo(BACK) } @Test fun newEducationToastOn1stEducation() = testScope.runTest { val model by collectLastValue(underTest.educationTriggered) triggerMaxEducationSignals(BACK) assertThat(model?.educationUiType).isEqualTo(EducationUiType.Toast) } @Test @kotlinx.coroutines.ExperimentalCoroutinesApi fun newEducationNotificationOn2ndEducation() = testScope.runTest { val model by collectLastValue(underTest.educationTriggered) triggerMaxEducationSignals(BACK) // runCurrent() to trigger 1st education runCurrent() triggerMaxEducationSignals(BACK) assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification) } @Test fun noEducationInfoBeforeMaxSignalCountReached() = testScope.runTest { Loading @@ -64,11 +91,30 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { testScope.runTest { val model by collectLastValue(underTest.educationTriggered) contextualEduInteractor.updateShortcutTriggerTime(BACK) tryTriggeringEducation(BACK) triggerMaxEducationSignals(BACK) assertThat(model).isNull() } private suspend fun tryTriggeringEducation(gestureType: GestureType) { @Test fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() = testScope.runTest { val model by collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK)) contextualEduInteractor.incrementSignalCount(BACK) eduClock.offset(KeyboardTouchpadEduInteractor.usageSessionDuration.plus(1.seconds)) val secondSignalReceivedTime = eduClock.instant() contextualEduInteractor.incrementSignalCount(BACK) assertThat(model) .isEqualTo( GestureEduModel( signalCount = 1, usageSessionStartTime = secondSignalReceivedTime ) ) } private suspend fun triggerMaxEducationSignals(gestureType: GestureType) { // Increment max number of signal to try triggering education for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) { contextualEduInteractor.incrementSignalCount(gestureType) Loading
packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -26,4 +26,6 @@ data class GestureEduModel( val signalCount: Int = 0, val educationShownCount: Int = 0, val lastShortcutTriggeredTime: Instant? = null, val usageSessionStartTime: Instant? = null, val lastEducationTime: Instant? = null, )
packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt +28 −2 Original line number Diff line number Diff line Loading @@ -73,6 +73,8 @@ constructor( const val SIGNAL_COUNT_SUFFIX = "_SIGNAL_COUNT" const val NUMBER_OF_EDU_SHOWN_SUFFIX = "_NUMBER_OF_EDU_SHOWN" const val LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX = "_LAST_SHORTCUT_TRIGGERED_TIME" const val USAGE_SESSION_START_TIME_SUFFIX = "_USAGE_SESSION_START_TIME" const val LAST_EDUCATION_TIME_SUFFIX = "_LAST_EDUCATION_TIME" const val DATASTORE_DIR = "education/USER%s_ContextualEducation" } Loading Loading @@ -113,6 +115,14 @@ constructor( preferences[getLastShortcutTriggeredTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, usageSessionStartTime = preferences[getUsageSessionStartTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, lastEducationTime = preferences[getLastEducationTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, ) } Loading @@ -125,11 +135,21 @@ constructor( val updatedModel = transform(currentModel) preferences[getSignalCountKey(gestureType)] = updatedModel.signalCount preferences[getEducationShownCountKey(gestureType)] = updatedModel.educationShownCount updateTimeByInstant( setInstant( preferences, updatedModel.lastShortcutTriggeredTime, getLastShortcutTriggeredTimeKey(gestureType) ) setInstant( preferences, updatedModel.usageSessionStartTime, getUsageSessionStartTimeKey(gestureType) ) setInstant( preferences, updatedModel.lastEducationTime, getLastEducationTimeKey(gestureType) ) } } Loading @@ -142,7 +162,13 @@ constructor( private fun getLastShortcutTriggeredTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX) private fun updateTimeByInstant( private fun getUsageSessionStartTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + USAGE_SESSION_START_TIME_SUFFIX) private fun getLastEducationTimeKey(gestureType: GestureType): Preferences.Key<Long> = longPreferencesKey(gestureType.name + LAST_EDUCATION_TIME_SUFFIX) private fun setInstant( preferences: MutablePreferences, instant: Instant?, key: Preferences.Key<Long> Loading
packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt +25 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,13 @@ constructor( } suspend fun incrementSignalCount(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy(signalCount = it.signalCount + 1) } repository.updateGestureEduModel(gestureType) { it.copy( signalCount = it.signalCount + 1, usageSessionStartTime = if (it.signalCount == 0) clock.instant() else it.usageSessionStartTime ) } } suspend fun updateShortcutTriggerTime(gestureType: GestureType) { Loading @@ -76,4 +82,22 @@ constructor( it.copy(lastShortcutTriggeredTime = clock.instant()) } } suspend fun updateOnEduTriggered(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy( // Reset signal counter and usageSessionStartTime after edu triggered signalCount = 0, lastEducationTime = clock.instant(), educationShownCount = it.educationShownCount + 1, usageSessionStartTime = null ) } } suspend fun startNewUsageSession(gestureType: GestureType) { repository.updateGestureEduModel(gestureType) { it.copy(usageSessionStartTime = clock.instant(), signalCount = 1) } } }