diff options
| author | 2025-02-12 18:53:19 +0000 | |
|---|---|---|
| committer | 2025-02-14 14:31:26 +0000 | |
| commit | 096c1cc8902d1822efdef9a9fd8c71ac55e45ab9 (patch) | |
| tree | bd33c1716f4c64f26dbaf91a23d471765c5dbbfe | |
| parent | eceea7f395c4da8fb309ce173ed80bec6e3b1295 (diff) | |
[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
2 files changed, 65 insertions, 23 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt index ff5fa3959c6d..7374f181760c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt index b712fdeaf623..0d57a57c3416 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt @@ -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() |