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

Commit 730b2e5c authored by yyalan's avatar yyalan
Browse files

Fix launched time. Add notified status.

Move launched time updating logic to where tutorial is launched. We need
it for for context edu. Add isNotified for oobe scheduling.

Drive-by renaming.

Bug: 378865553
Flag: NONE bug fix
Test: TutorialSchedulerInteractorTest
Change-Id: Iae15120a6e9c26c1ea849d5c9ce43eef8e8ed6f5
parent 84f5f8ff
Loading
Loading
Loading
Loading
+42 −31
Original line number Diff line number Diff line
@@ -22,13 +22,12 @@ import com.android.systemui.SysuiTestCase
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 com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.time.Instant
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,7 +37,7 @@ import org.junit.runner.RunWith
class TutorialSchedulerRepositoryTest : SysuiTestCase() {

    private lateinit var underTest: TutorialSchedulerRepository
    private val kosmos = Kosmos()
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    @Before
@@ -46,45 +45,57 @@ class TutorialSchedulerRepositoryTest : SysuiTestCase() {
        underTest =
            TutorialSchedulerRepository(
                context,
                testScope.backgroundScope,
                kosmos.backgroundScope,
                "TutorialSchedulerRepositoryTest",
            )
    }

    @After
    fun clear() {
        testScope.launch { underTest.clear() }
    }

    @Test
    fun initialState() =
        testScope.runTest {
    fun initialState() = runTestAndClear {
        assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse()
        assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
            assertThat(underTest.isLaunched(KEYBOARD)).isFalse()
            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
        assertThat(underTest.isNotified(KEYBOARD)).isFalse()
        assertThat(underTest.isNotified(TOUCHPAD)).isFalse()
        assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isFalse()
        assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse()
    }

    @Test
    fun connectKeyboard() =
        testScope.runTest {
    fun connectKeyboard() = runTestAndClear {
        val now = Instant.now()
            underTest.updateFirstConnectionTime(KEYBOARD, now)
        underTest.setFirstConnectionTime(KEYBOARD, now)

        assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue()
            assertThat(underTest.firstConnectionTime(KEYBOARD)!!.epochSecond)
        assertThat(underTest.getFirstConnectionTime(KEYBOARD)!!.epochSecond)
            .isEqualTo(now.epochSecond)
        assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
    }

    @Test
    fun launchKeyboard() =
        testScope.runTest {
    fun launchKeyboard() = runTestAndClear {
        val now = Instant.now()
            underTest.updateLaunchTime(KEYBOARD, now)
        underTest.setScheduledTutorialLaunchTime(KEYBOARD, now)

        assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isTrue()
        assertThat(underTest.getScheduledTutorialLaunchTime(KEYBOARD)!!.epochSecond)
            .isEqualTo(now.epochSecond)
        assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse()
    }

    @Test
    fun notifyKeyboard() = runTestAndClear {
        underTest.setNotified(KEYBOARD)

            assertThat(underTest.isLaunched(KEYBOARD)).isTrue()
            assertThat(underTest.launchTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond)
            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
        assertThat(underTest.isNotified(KEYBOARD)).isTrue()
        assertThat(underTest.isNotified(TOUCHPAD)).isFalse()
    }

    private fun runTestAndClear(block: suspend () -> Unit) =
        testScope.runTest {
            try {
                block()
            } finally {
                underTest.clear()
            }
        }
}
+39 −45
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedul
import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.userTracker
@@ -34,14 +35,9 @@ import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.hours
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -65,7 +61,6 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    private val testScope = kosmos.testScope
    private val keyboardRepository = FakeKeyboardRepository()
    private val touchpadRepository = FakeTouchpadRepository()
    private lateinit var dataStoreScope: CoroutineScope
    private lateinit var repository: TutorialSchedulerRepository
    @Mock private lateinit var notificationManager: NotificationManager
    @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification>
@@ -73,11 +68,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {

    @Before
    fun setup() {
        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
        repository =
            TutorialSchedulerRepository(
                context,
                dataStoreScope,
                kosmos.backgroundScope,
                dataStoreName = "TutorialNotificationCoordinatorTest",
            )
        val interactor =
@@ -87,6 +81,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
                repository,
                kosmos.inputDeviceTutorialLogger,
                kosmos.commandRegistry,
                testScope.backgroundScope,
            )
        underTest =
            TutorialNotificationCoordinator(
@@ -100,17 +95,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
        underTest.start()
    }

    @After
    fun clear() {
        runBlocking { repository.clear() }
        dataStoreScope.cancel()
    }

    @Test
    fun showKeyboardNotification() =
        testScope.runTest {
    fun showKeyboardNotification() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
            advanceTimeBy(LAUNCH_DELAY)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        verifyNotification(
            R.string.launch_keyboard_tutorial_notification_title,
            R.string.launch_keyboard_tutorial_notification_content,
@@ -118,10 +106,9 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    }

    @Test
    fun showTouchpadNotification() =
        testScope.runTest {
    fun showTouchpadNotification() = runTestAndClear {
        touchpadRepository.setIsAnyTouchpadConnected(true)
            advanceTimeBy(LAUNCH_DELAY)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        verifyNotification(
            R.string.launch_touchpad_tutorial_notification_title,
            R.string.launch_touchpad_tutorial_notification_content,
@@ -129,11 +116,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    }

    @Test
    fun showKeyboardTouchpadNotification() =
        testScope.runTest {
    fun showKeyboardTouchpadNotification() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        touchpadRepository.setIsAnyTouchpadConnected(true)
            advanceTimeBy(LAUNCH_DELAY)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        verifyNotification(
            R.string.launch_keyboard_touchpad_tutorial_notification_title,
            R.string.launch_keyboard_touchpad_tutorial_notification_content,
@@ -141,13 +127,21 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    }

    @Test
    fun doNotShowNotification() =
        testScope.runTest {
            advanceTimeBy(LAUNCH_DELAY)
    fun doNotShowNotification() = runTestAndClear {
        testScope.advanceTimeBy(LAUNCH_DELAY)
        verify(notificationManager, never())
            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any())
    }

    private fun runTestAndClear(block: suspend () -> Unit) =
        testScope.runTest {
            try {
                block()
            } finally {
                repository.clear()
            }
        }

    private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) {
        verify(notificationManager)
            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any())
+56 −66
Original line number Diff line number Diff line
@@ -30,16 +30,11 @@ import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.hours
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,18 +47,16 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() {
    private lateinit var underTest: TutorialSchedulerInteractor
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private lateinit var dataStoreScope: CoroutineScope
    private val keyboardRepository = FakeKeyboardRepository()
    private val touchpadRepository = FakeTouchpadRepository()
    private lateinit var schedulerRepository: TutorialSchedulerRepository

    @Before
    fun setup() {
        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
        schedulerRepository =
            TutorialSchedulerRepository(
                context,
                dataStoreScope,
                testScope.backgroundScope,
                dataStoreName = "TutorialSchedulerInteractorTest",
            )
        underTest =
@@ -73,98 +66,95 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() {
                schedulerRepository,
                kosmos.inputDeviceTutorialLogger,
                kosmos.commandRegistry,
                testScope.backgroundScope,
            )
    }

    @After
    fun clear() {
        runBlocking { schedulerRepository.clear() }
        dataStoreScope.cancel()
    }

    @Test
    fun connectKeyboard_delayElapse_launchForKeyboard() =
        testScope.runTest {
    fun connectKeyboard_delayElapse_notifyForKeyboard() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
            advanceTimeBy(LAUNCH_DELAY)

            launchAndAssert(TutorialType.KEYBOARD)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        notifyAndAssert(TutorialType.KEYBOARD)
    }

    @Test
    fun connectBothDevices_delayElapse_launchForBoth() =
        testScope.runTest {
    fun connectBothDevices_delayElapse_notifyForBoth() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        touchpadRepository.setIsAnyTouchpadConnected(true)
            advanceTimeBy(LAUNCH_DELAY)
        testScope.advanceTimeBy(LAUNCH_DELAY)

            launchAndAssert(TutorialType.BOTH)
        notifyAndAssert(TutorialType.BOTH)
    }

    @Test
    fun connectBothDevice_delayNotElapse_launchNothing() =
        testScope.runTest {
    fun connectBothDevice_delayNotElapse_notifyNothing() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        touchpadRepository.setIsAnyTouchpadConnected(true)
            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)

            launchAndAssert(TutorialType.NONE)
        notifyAndAssert(TutorialType.NONE)
    }

    @Test
    fun nothingConnect_delayElapse_launchNothing() =
        testScope.runTest {
    fun nothingConnect_delayElapse_notifyNothing() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(false)
        touchpadRepository.setIsAnyTouchpadConnected(false)
            advanceTimeBy(LAUNCH_DELAY)
        testScope.advanceTimeBy(LAUNCH_DELAY)

            launchAndAssert(TutorialType.NONE)
        notifyAndAssert(TutorialType.NONE)
    }

    @Test
    fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
        testScope.runTest {
    fun connectKeyboard_thenTouchpad_delayElapse_notifyForBoth() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
        touchpadRepository.setIsAnyTouchpadConnected(true)
            advanceTimeBy(REMAINING_TIME)
        testScope.advanceTimeBy(REMAINING_TIME)

            launchAndAssert(TutorialType.BOTH)
        notifyAndAssert(TutorialType.BOTH)
    }

    @Test
    fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
        testScope.runTest {
    fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_notifyNothing() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
        touchpadRepository.setIsAnyTouchpadConnected(true)
        keyboardRepository.setIsAnyKeyboardConnected(false)
            advanceTimeBy(REMAINING_TIME)
            launchAndAssert(TutorialType.NONE)
        testScope.advanceTimeBy(REMAINING_TIME)

        notifyAndAssert(TutorialType.NONE)
    }

    private fun runTestAndClear(block: suspend () -> Unit) =
        testScope.runTest {
            try {
                block()
            } finally {
                schedulerRepository.clear()
            }
        }

    private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
    private fun notifyAndAssert(expectedTutorial: TutorialType) =
        testScope.backgroundScope.launch {
            val actualTutorial = underTest.tutorials.first()
            assertThat(actualTutorial).isEqualTo(expectedTutorial)

            // TODO: need to update after we move launch into the tutorial
            when (expectedTutorial) {
                TutorialType.KEYBOARD -> {
                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue()
                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse()
                }
                TutorialType.TOUCHPAD -> {
                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse()
                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue()
                }
                TutorialType.BOTH -> {
                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue()
                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue()
                }
                TutorialType.NONE -> {
                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse()
                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse()
                }
            }
        }
+3 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.education.domain.interactor

import android.os.SystemProperties
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.contextualeducation.GestureType
@@ -56,7 +57,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.merge
import com.android.app.tracing.coroutines.launchTraced as launch

/** Allow listening to new contextual education triggered */
@SysUISingleton
@@ -278,7 +278,8 @@ constructor(
        }

    private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean {
        val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false
        val oobeLaunchTime =
            tutorialRepository.getScheduledTutorialLaunchTime(deviceType) ?: return false
        return clock
            .instant()
            .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds))
+6 −3
Original line number Diff line number Diff line
@@ -20,14 +20,17 @@ import java.time.Instant

data class DeviceSchedulerInfo(
    var launchTime: Instant? = null,
    var firstConnectionTime: Instant? = null
    var firstConnectionTime: Instant? = null,
    var isNotified: Boolean = false,
) {
    constructor(
        launchTimeSec: Long?,
        firstConnectionTimeSec: Long?
        firstConnectionTimeSec: Long?,
        isNotified: Boolean = false,
    ) : this(
        launchTimeSec?.let { Instant.ofEpochSecond(it) },
        firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) }
        firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) },
        isNotified,
    )

    val wasEverConnected: Boolean
Loading