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

Commit 096c1cc8 authored by yyalan's avatar yyalan
Browse files

[OOBE] Update the notification

Handle cases like: show keyboard notification then a touchpad, and the
notification should be updated for both

Fixes: 394779964
Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial
Test: TutorialNotificationCoordinatorTest.kt
Change-Id: Icce3e9ddc77e2c570eb41f8ed981494ea8210f36
parent eceea7f3
Loading
Loading
Loading
Loading
+62 −18
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ 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.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -51,7 +50,6 @@ import org.mockito.Mockito.times
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.firstValue
import org.mockito.kotlin.never
import org.mockito.kotlin.secondValue
import org.mockito.kotlin.verify
@@ -116,7 +114,6 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    fun showTouchpadNotification() = runTestAndClear {
        touchpadRepository.setIsAnyTouchpadConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        mockExistingNotification()
        verifyNotification(
            R.string.launch_touchpad_tutorial_notification_title,
            R.string.launch_touchpad_tutorial_notification_content,
@@ -142,14 +139,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    }

    @Test
    fun showKeyboardNotificationThenDisconnectKeyboard() = runTestAndClear {
    fun cancelKeyboardNotificationWhenKeyboardDisconnects() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        verifyNotification(
            R.string.launch_keyboard_tutorial_notification_title,
            R.string.launch_keyboard_tutorial_notification_content,
        )
        mockExistingNotification()
        mockNotifications(hasTutorialNotification = true)

        // After the keyboard is disconnected, i.e. there is nothing connected, the notification
        // should be cancelled
@@ -158,22 +151,71 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
    }

    @Test
    fun showKeyboardTouchpadNotificationThenDisconnectKeyboard() = runTestAndClear {
    fun updateNotificationToTouchpadOnlyWhenKeyboardDisconnects() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        touchpadRepository.setIsAnyTouchpadConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        mockExistingNotification()
        mockNotifications(hasTutorialNotification = true)

        keyboardRepository.setIsAnyKeyboardConnected(false)

        verify(notificationManager, times(2))
            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any())
        // Connect both device and the first notification is for both
        notificationCaptor.firstValue.verify(
        // Connect both device and the first notification is for both. After the keyboard is
        // disconnected, i.e. with only the touchpad left, the notification should be update to
        // touchpad only
        notificationCaptor.secondValue.verify(
            R.string.launch_touchpad_tutorial_notification_title,
            R.string.launch_touchpad_tutorial_notification_content,
        )
    }

    @Test
    fun updateNotificationToBothDevicesWhenTouchpadConnects() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        mockNotifications(hasTutorialNotification = true)

        touchpadRepository.setIsAnyTouchpadConnected(true)

        verify(notificationManager, times(2))
            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any())
        // Update the notification from keyboard to both devices
        notificationCaptor.secondValue.verify(
            R.string.launch_keyboard_touchpad_tutorial_notification_title,
            R.string.launch_keyboard_touchpad_tutorial_notification_content,
        )
        // After the keyboard is disconnected, i.e. with only the touchpad left, the notification
        // should be update to the one for only touchpad
    }

    @Test
    fun doNotShowUpdateNotificationWhenInitialNotificationIsDismissed() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        mockNotifications(hasTutorialNotification = false)

        touchpadRepository.setIsAnyTouchpadConnected(true)

        // There's only one notification being shown throughout this scenario. We don't update the
        // notification because it has been dismissed when the touchpad connects
        verifyNotification(
            R.string.launch_keyboard_tutorial_notification_title,
            R.string.launch_keyboard_tutorial_notification_content,
        )
    }

    @Test
    fun showTouchpadNotificationAfterDelayAndKeyboardNotificationIsDismissed() = runTestAndClear {
        keyboardRepository.setIsAnyKeyboardConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)
        mockNotifications(hasTutorialNotification = false)

        touchpadRepository.setIsAnyTouchpadConnected(true)
        testScope.advanceTimeBy(LAUNCH_DELAY)

        verify(notificationManager, times(2))
            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any())
        // The keyboard notification was shown and dismissed; the touchpad notification is scheduled
        // independently
        notificationCaptor.secondValue.verify(
            R.string.launch_touchpad_tutorial_notification_title,
            R.string.launch_touchpad_tutorial_notification_content,
@@ -189,10 +231,12 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() {
            }
        }

    // Assume that there's an existing notification when the updater checks activeNotifications
    private fun mockExistingNotification() {
    // Mock an active notification, so when the updater checks activeNotifications, it returns one
    // with the given id. Otherwise, return an empty array (i.e. no active notifications)
    private fun mockNotifications(hasTutorialNotification: Boolean) {
        whenever(notification.id).thenReturn(NOTIFICATION_ID)
        whenever(notificationManager.activeNotifications).thenReturn(arrayOf(notification))
        val notifications = if (hasTutorialNotification) arrayOf(notification) else emptyArray()
        whenever(notificationManager.activeNotifications).thenReturn(notifications)
    }

    private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) {
+3 −5
Original line number Diff line number Diff line
@@ -102,10 +102,9 @@ constructor(
    }

    // This flow is used by the notification updater once an initial notification is launched. It
    // listens to the device connection changes for both keyboard and touchpad. When either of the
    // device is disconnected, resolve the tutorial type base on the latest connection state.
    // Dropping the initial state because it's the existing notification. Filtering out BOTH because
    // we only care about disconnections.
    // listens to the changes of keyboard and touchpad connection and resolve the tutorial type base
    // on the latest connection state.
    // Dropping the initial state because it represents the existing notification.
    val tutorialTypeUpdates: Flow<TutorialType> =
        keyboardRepository.isAnyKeyboardConnected
            .combine(touchpadRepository.isAnyTouchpadConnected, ::Pair)
@@ -118,7 +117,6 @@ constructor(
                }
            }
            .drop(1)
            .filter { it != TutorialType.BOTH }

    private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
        isAnyDeviceConnected[deviceType]!!.filter { it }.first()