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

Commit 83c2cacb authored by helencheuk's avatar helencheuk
Browse files

[Contextual Edu] Add PKT education shortcut time field in repo and its kosmos

- Add update shortcut time logic
- Add related kosmos classes

Test: ContextualEducationRepositoryTest
Bug: 317496783
Flag: com.android.systemui.keyboard_touchpad_contextual_education
Change-Id: I678c60dd83a6deea3f7333e937a1ef3c8700b19e
parent be9dc46c
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.education.GestureType.BACK_GESTURE
import com.google.common.truth.Truth.assertThat
import java.io.File
import java.time.Clock
import java.time.Instant
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.TestScope
@@ -48,6 +50,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
    private val dsScopeProvider: Provider<CoroutineScope> = Provider {
        TestScope(kosmos.testDispatcher).backgroundScope
    }
    private val clock: Clock = FakeEduClock(Instant.ofEpochMilli(1000))
    private val testUserId = 1111

    // For deleting any test files created after the test
@@ -59,7 +62,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
        // needed before calling TemporaryFolder.newFolder().
        val testContext = TestContext(context, tmpFolder.newFolder())
        val userRepository = UserContextualEducationRepository(testContext, dsScopeProvider)
        underTest = ContextualEducationRepository(userRepository)
        underTest = ContextualEducationRepositoryImpl(clock, userRepository)
        underTest.setUser(testUserId)
    }

@@ -85,6 +88,15 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
            assertThat(model?.signalCount).isEqualTo(1)
        }

    @Test
    fun dataAddedOnUpdateShortcutTriggerTime() =
        testScope.runTest {
            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK_GESTURE))
            assertThat(model?.lastShortcutTriggeredTime).isNull()
            underTest.updateShortcutTriggerTime(BACK_GESTURE)
            assertThat(model?.lastShortcutTriggeredTime).isEqualTo(clock.instant())
        }

    /** Test context which allows overriding getFilesDir path */
    private class TestContext(context: Context, private val folder: File) :
        SysuiTestableContext(context) {
+17 −0
Original line number Diff line number Diff line
@@ -17,8 +17,12 @@
package com.android.systemui.education.dagger

import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.data.repository.ContextualEducationRepository
import com.android.systemui.education.data.repository.ContextualEducationRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
import java.time.Clock
import javax.inject.Qualifier
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -26,8 +30,15 @@ import kotlinx.coroutines.SupervisorJob

@Module
interface ContextualEducationModule {
    @Binds
    fun bindContextualEducationRepository(
        impl: ContextualEducationRepositoryImpl
    ): ContextualEducationRepository

    @Qualifier annotation class EduDataStoreScope

    @Qualifier annotation class EduClock

    companion object {
        @EduDataStoreScope
        @Provides
@@ -36,5 +47,11 @@ interface ContextualEducationModule {
        ): CoroutineScope {
            return CoroutineScope(bgDispatcher + SupervisorJob())
        }

        @EduClock
        @Provides
        fun provideEduClock(): Clock {
            return Clock.systemUTC()
        }
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.systemui.education.data.model

import java.time.Instant

/**
 * Model to store education data related to each gesture (e.g. Back, Home, All Apps, Overview). Each
 * gesture stores its own model separately.
 */
data class GestureEduModel(
    val signalCount: Int,
    val educationShownCount: Int,
    val signalCount: Int = 0,
    val educationShownCount: Int = 0,
    val lastShortcutTriggeredTime: Instant? = null,
)
+29 −5
Original line number Diff line number Diff line
@@ -17,26 +17,50 @@
package com.android.systemui.education.data.repository

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.shared.education.GestureType
import java.time.Clock
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow

/** Encapsulates the functions of ContextualEducationRepository. */
interface ContextualEducationRepository {
    fun setUser(userId: Int)

    fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel>

    suspend fun incrementSignalCount(gestureType: GestureType)

    suspend fun updateShortcutTriggerTime(gestureType: GestureType)
}

/**
 * Provide methods to read and update on field level and allow setting datastore when user is
 * changed
 */
@SysUISingleton
class ContextualEducationRepository
class ContextualEducationRepositoryImpl
@Inject
constructor(private val userEduRepository: UserContextualEducationRepository) {
constructor(
    @EduClock private val clock: Clock,
    private val userEduRepository: UserContextualEducationRepository
) : ContextualEducationRepository {
    /** To change data store when user is changed */
    fun setUser(userId: Int) = userEduRepository.setUser(userId)
    override fun setUser(userId: Int) = userEduRepository.setUser(userId)

    fun readGestureEduModelFlow(gestureType: GestureType) =
    override fun readGestureEduModelFlow(gestureType: GestureType) =
        userEduRepository.readGestureEduModelFlow(gestureType)

    suspend fun incrementSignalCount(gestureType: GestureType) {
    override suspend fun incrementSignalCount(gestureType: GestureType) {
        userEduRepository.updateGestureEduModel(gestureType) {
            it.copy(signalCount = it.signalCount + 1)
        }
    }

    override suspend fun updateShortcutTriggerTime(gestureType: GestureType) {
        userEduRepository.updateGestureEduModel(gestureType) {
            it.copy(lastShortcutTriggeredTime = clock.instant())
        }
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -18,16 +18,19 @@ package com.android.systemui.education.data.repository

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.preferencesDataStoreFile
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.education.dagger.ContextualEducationModule.EduDataStoreScope
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.shared.education.GestureType
import java.time.Instant
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
@@ -55,6 +58,7 @@ constructor(
    companion object {
        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 DATASTORE_DIR = "education/USER%s_ContextualEducation"
    }
@@ -91,6 +95,10 @@ constructor(
        return GestureEduModel(
            signalCount = preferences[getSignalCountKey(gestureType)] ?: 0,
            educationShownCount = preferences[getEducationShownCountKey(gestureType)] ?: 0,
            lastShortcutTriggeredTime =
                preferences[getLastShortcutTriggeredTimeKey(gestureType)]?.let {
                    Instant.ofEpochMilli(it)
                },
        )
    }

@@ -103,6 +111,11 @@ constructor(
            val updatedModel = transform(currentModel)
            preferences[getSignalCountKey(gestureType)] = updatedModel.signalCount
            preferences[getEducationShownCountKey(gestureType)] = updatedModel.educationShownCount
            updateTimeByInstant(
                preferences,
                updatedModel.lastShortcutTriggeredTime,
                getLastShortcutTriggeredTimeKey(gestureType)
            )
        }
    }

@@ -111,4 +124,19 @@ constructor(

    private fun getEducationShownCountKey(gestureType: GestureType): Preferences.Key<Int> =
        intPreferencesKey(gestureType.name + NUMBER_OF_EDU_SHOWN_SUFFIX)

    private fun getLastShortcutTriggeredTimeKey(gestureType: GestureType): Preferences.Key<Long> =
        longPreferencesKey(gestureType.name + LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX)

    private fun updateTimeByInstant(
        preferences: MutablePreferences,
        instant: Instant?,
        key: Preferences.Key<Long>
    ) {
        if (instant != null) {
            preferences[key] = instant.toEpochMilli()
        } else {
            preferences.remove(key)
        }
    }
}
Loading