Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a19b0f2b authored by helencheuk's avatar helencheuk
Browse files

[Contextual Edu] Update data on education triggered

When education is triggered, reset signal count and the usage session time, increase edu count by 1.
Check if the usage session expires when new signal count is received, restart a new session if expired.

Test: KeyboardTouchpadEduInteractorTest
Test: ContextualEducationRepositoryTest
Bug: 354884305
Flag: com.android.systemui.keyboard_touchpad_contextual_education
Change-Id: I2fa4b5c71134fb210377fc13a32f8864891111ca
parent 67401379
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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))
+49 −3
Original line number Diff line number Diff line
@@ -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
@@ -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() {
@@ -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 {
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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,
)
+28 −2
Original line number Diff line number Diff line
@@ -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"
    }
@@ -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)
                },
        )
    }

@@ -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)
            )
        }
    }

@@ -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>
+25 −1
Original line number Diff line number Diff line
@@ -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) {
@@ -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