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

Commit 8b9bb07a authored by Helen Cheuk's avatar Helen Cheuk Committed by Android (Google) Code Review
Browse files

Merge "[Contextual Edu] Display edu notification" into main

parents 1ec5df60 762af00c
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
    }

    private val testUserId = 1111
    private val secondTestUserId = 1112

    // For deleting any test files created after the test
    @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
@@ -73,11 +74,20 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
            assertThat(model?.signalCount).isEqualTo(1)

            // User is changed.
            underTest.setUser(1112)
            underTest.setUser(secondTestUserId)
            // Assert count is 0 after user is changed.
            assertThat(model?.signalCount).isEqualTo(0)
        }

    @Test
    fun changeUserIdForNewUser() =
        testScope.runTest {
            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
            assertThat(model?.userId).isEqualTo(testUserId)
            underTest.setUser(secondTestUserId)
            assertThat(model?.userId).isEqualTo(secondTestUserId)
        }

    @Test
    fun dataChangedOnUpdate() =
        testScope.runTest {
@@ -88,6 +98,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() {
                    lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(),
                    lastEducationTime = kosmos.fakeEduClock.instant(),
                    usageSessionStartTime = kosmos.fakeEduClock.instant(),
                    userId = testUserId
                )
            underTest.updateGestureEduModel(BACK) { newModel }
            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
+2 −1
Original line number Diff line number Diff line
@@ -109,7 +109,8 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
                .isEqualTo(
                    GestureEduModel(
                        signalCount = 1,
                        usageSessionStartTime = secondSignalReceivedTime
                        usageSessionStartTime = secondSignalReceivedTime,
                        userId = 0
                    )
                )
        }
+68 −5
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.education.domain.ui.view

import android.app.Notification
import android.app.NotificationManager
import android.content.applicationContext
import android.widget.Toast
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,27 +32,35 @@ import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class ContextualEduUiCoordinatorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val interactor = kosmos.contextualEducationInteractor
    private lateinit var underTest: ContextualEduUiCoordinator
    @Mock private lateinit var toast: Toast

    @Mock private lateinit var notificationManager: NotificationManager
    @get:Rule val mockitoRule = MockitoJUnit.rule()
    private var toastContent = ""

    @Before
    fun setUp() {
@@ -60,23 +70,76 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() {
                kosmos.keyboardTouchpadEduInteractor
            )
        underTest =
            ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast }
            ContextualEduUiCoordinator(
                kosmos.applicationCoroutineScope,
                viewModel,
                kosmos.applicationContext,
                notificationManager
            ) { content ->
                toastContent = content
                toast
            }
        underTest.start()
        kosmos.keyboardTouchpadEduInteractor.start()
    }

    @Test
    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
    fun showToastOnNewEdu() =
        testScope.runTest {
            triggerEducation(BACK)
            runCurrent()
            verify(toast).show()
        }

    private suspend fun triggerEducation(gestureType: GestureType) {
    @Test
    fun showNotificationOn2ndEdu() =
        testScope.runTest {
            triggerEducation(BACK)
            triggerEducation(BACK)
            verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any())
        }

    @Test
    fun verifyBackEduToastContent() =
        testScope.runTest {
            triggerEducation(BACK)
            assertThat(toastContent).isEqualTo(context.getString(R.string.back_edu_toast_content))
        }

    @Test
    fun verifyBackEduNotificationContent() =
        testScope.runTest {
            val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
            triggerEducation(BACK)
            triggerEducation(BACK)
            verify(notificationManager)
                .notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any())
            verifyNotificationContent(
                R.string.back_edu_notification_title,
                R.string.back_edu_notification_content,
                notificationCaptor.value
            )
        }

    private fun verifyNotificationContent(
        titleResId: Int,
        contentResId: Int,
        notification: Notification
    ) {
        val expectedContent = context.getString(contentResId)
        val expectedTitle = context.getString(titleResId)
        val actualContent = notification.getString(Notification.EXTRA_TEXT)
        val actualTitle = notification.getString(Notification.EXTRA_TITLE)
        assertThat(actualContent).isEqualTo(expectedContent)
        assertThat(actualTitle).isEqualTo(expectedTitle)
    }

    private fun Notification.getString(key: String): String =
        this.extras?.getCharSequence(key).toString()

    private suspend fun TestScope.triggerEducation(gestureType: GestureType) {
        for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
            interactor.incrementSignalCount(gestureType)
        }
        runCurrent()
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -28,4 +28,5 @@ data class GestureEduModel(
    val lastShortcutTriggeredTime: Instant? = null,
    val usageSessionStartTime: Instant? = null,
    val lastEducationTime: Instant? = null,
    val userId: Int
)
+5 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.systemui.education.data.model.GestureEduModel
import java.time.Instant
import javax.inject.Inject
import javax.inject.Provider
import kotlin.properties.Delegates.notNull
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
@@ -79,6 +80,8 @@ constructor(
        const val DATASTORE_DIR = "education/USER%s_ContextualEducation"
    }

    private var userId by notNull<Int>()

    private var dataStoreScope: CoroutineScope? = null

    private val datastore = MutableStateFlow<DataStore<Preferences>?>(null)
@@ -89,6 +92,7 @@ constructor(
    override fun setUser(userId: Int) {
        dataStoreScope?.cancel()
        val newDsScope = dataStoreScopeProvider.get()
        this.userId = userId
        datastore.value =
            PreferenceDataStoreFactory.create(
                produceFile = {
@@ -123,6 +127,7 @@ constructor(
                preferences[getLastEducationTimeKey(gestureType)]?.let {
                    Instant.ofEpochSecond(it)
                },
            userId = userId
        )
    }

Loading