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

Commit 881fb5b0 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

Include screen recording notifs in promoted section.

The promoted section of the notification shade should generally match
the status bar chips, where any status bar chip also has a notification
in the shade and the chip ranking determines the shade ranking.

This CL updates the promoted section of the shade to include screen
recording notifications if we can find a good match for them. If we did
find a screen recording notification, it'll be ranked at the top of the
list.

Fixes: 395151171
Bug: 367705002
Flag: android.app.ui_rich_ongoing
Test: Start screen recording with an app that posts a notification but
not a promoted one -> verify its notif appears at the top of the shade
in its own section. Then also trigger a phone call -> verify the
promoted notification section shows screen record notif first, then call
notif
Test: Start & end a SysUI screen record so you have a "Recording saved"
notification. Then start another SysUI screen recording -> verify
SysUI's screen recording notification appears in the promoted section

Test: atest PromotedNotificationsInteractorTest
ScreenRecordChipInteractorTest ColorizedFgsCoordinatorTest

Change-Id: Ida64c77c8ebb335cb7116d64442aa383d9806b4c
parent 55c43cbe
Loading
Loading
Loading
Loading
+49 −7
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting

            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
            assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull()
        }

    @Test
@@ -99,7 +99,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen("host.package")

            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
            assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull()
        }

    @Test
@@ -116,7 +116,48 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
                    task,
                )

            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
            assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)
        }

    @Test
    fun screenRecordState_projectionIsNotProjecting_hostPackageNull() =
        testScope.runTest {
            val latest by collectLastValue(underTest.screenRecordState)

            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting

            assertThat((latest as ScreenRecordChipModel.Recording).hostPackage).isNull()
        }

    @Test
    fun screenRecordState_projectionIsEntireScreen_hostPackageMatches() =
        testScope.runTest {
            val latest by collectLastValue(underTest.screenRecordState)

            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(hostPackage = "host.package")

            assertThat((latest as ScreenRecordChipModel.Recording).hostPackage)
                .isEqualTo("host.package")
        }

    @Test
    fun screenRecordState_projectionIsSingleTask_hostPackageMatches() =
        testScope.runTest {
            val latest by collectLastValue(underTest.screenRecordState)

            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
            mediaProjectionRepo.mediaProjectionState.value =
                MediaProjectionState.Projecting.SingleTask(
                    hostPackage = "host.package",
                    hostDeviceName = null,
                    task = createTask(taskId = 1),
                )

            assertThat((latest as ScreenRecordChipModel.Recording).hostPackage)
                .isEqualTo("host.package")
        }

    @Test
@@ -150,7 +191,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
            advanceTimeBy(901)

            // THEN we automatically update to the recording state
            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
            assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
        }

    @Test
@@ -175,13 +216,14 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
                )

            // THEN we immediately switch to Recording, and we have the task
            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
            assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
            assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)

            // WHEN more than 900ms has elapsed
            advanceTimeBy(200)

            // THEN we still stay in the Recording state and we have the task
            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
            assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)
        }

    @Test
@@ -247,7 +289,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {

            // THEN we *do* auto-start 400ms later
            advanceTimeBy(401)
            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
            assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
        }

    @Test
+5 −0
Original line number Diff line number Diff line
@@ -1159,6 +1159,11 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
                .inOrder()
        }

    // The ranking between different chips should stay consistent between
    // OngoingActivityChipsViewModel and PromotedNotificationsInteractor.
    // Make sure to also change
    // PromotedNotificationsInteractorTest#orderedChipNotificationKeys_rankingIsCorrect
    // if you change this test.
    @EnableChipsModernization
    @Test
    fun chips_screenRecordAndCallAndPromotedNotifs_secondNotifInOverflow() =
+86 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator

import android.app.Notification
import android.app.Notification.FLAG_FOREGROUND_SERVICE
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Person
@@ -31,6 +32,10 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.core.StatusBarRootModernization
@@ -167,6 +172,87 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() {
        assertFalse(sectioner.isInSection(entry))
    }

    @Test
    @EnableFlags(PromotedNotificationUi.FLAG_NAME)
    fun testIncludeScreenRecordNotifInSection_importanceDefault() =
        kosmos.runTest {
            // GIVEN a screen record event + screen record notif that has a status bar chip
            screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
            fakeMediaProjectionRepository.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
            val screenRecordEntry =
                buildNotificationEntry(tag = "screenRecord", promoted = false) {
                    setImportance(NotificationManager.IMPORTANCE_DEFAULT)
                    setFlag(context, FLAG_FOREGROUND_SERVICE, true)
                }

            renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))

            val orderedChipNotificationKeys by
                collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)

            assertThat(orderedChipNotificationKeys)
                .containsExactly("0|test_pkg|0|screenRecord|0")
                .inOrder()

            // THEN the entry is in the fgs section
            assertTrue(sectioner.isInSection(screenRecordEntry))
        }

    @Test
    @EnableFlags(PromotedNotificationUi.FLAG_NAME)
    fun testDiscludeScreenRecordNotifInSection_importanceMin() =
        kosmos.runTest {
            // GIVEN a screen record event + screen record notif that has a status bar chip
            screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
            fakeMediaProjectionRepository.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
            val screenRecordEntry =
                buildNotificationEntry(tag = "screenRecord", promoted = false) {
                    setImportance(NotificationManager.IMPORTANCE_MIN)
                    setFlag(context, FLAG_FOREGROUND_SERVICE, true)
                }

            renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))

            val orderedChipNotificationKeys by
                collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)

            assertThat(orderedChipNotificationKeys)
                .containsExactly("0|test_pkg|0|screenRecord|0")
                .inOrder()

            // THEN the entry is NOT in the fgs section
            assertFalse(sectioner.isInSection(screenRecordEntry))
        }

    @Test
    @DisableFlags(PromotedNotificationUi.FLAG_NAME)
    fun testDiscludeScreenRecordNotifInSection_flagDisabled() =
        kosmos.runTest {
            // GIVEN a screen record event + screen record notif that has a status bar chip
            screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
            fakeMediaProjectionRepository.mediaProjectionState.value =
                MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
            val screenRecordEntry =
                buildNotificationEntry(tag = "screenRecord", promoted = false) {
                    setImportance(NotificationManager.IMPORTANCE_DEFAULT)
                    setFlag(context, FLAG_FOREGROUND_SERVICE, true)
                }

            renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))

            val orderedChipNotificationKeys by
                collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)

            assertThat(orderedChipNotificationKeys)
                .containsExactly("0|test_pkg|0|screenRecord|0")
                .inOrder()

            // THEN the entry is NOT in the fgs section
            assertFalse(sectioner.isInSection(screenRecordEntry))
        }

    @Test
    @EnableFlags(PromotedNotificationUi.FLAG_NAME)
    fun promoterSelectsPromotedOngoing_flagEnabled() {
@@ -234,8 +320,4 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() {
        val person = Person.Builder().setName("person").build()
        return Notification.CallStyle.forOngoingCall(person, pendingIntent)
    }

    companion object {
        private const val NOTIF_USER_ID = 0
    }
}
+529 −0

File changed.

Preview size limit exceeded, changes collapsed.

+19 −5
Original line number Diff line number Diff line
@@ -94,9 +94,11 @@ constructor(
                        TAG,
                        LogLevel.INFO,
                        {},
                        { "State: Recording(taskPackage=null) due to force-start" },
                        {
                            "State: Recording(hostPackage=null, taskPackage=null) due to force-start"
                        },
                    )
                    ScreenRecordChipModel.Recording(recordedTask = null)
                    ScreenRecordChipModel.Recording(hostPackage = null, recordedTask = null)
                } else {
                    when (screenRecordState) {
                        is ScreenRecordModel.DoingNothing -> {
@@ -124,13 +126,25 @@ constructor(
                                } else {
                                    null
                                }
                            val hostPackage =
                                if (mediaProjectionState is MediaProjectionState.Projecting) {
                                    mediaProjectionState.hostPackage
                                } else {
                                    null
                                }
                            logger.log(
                                TAG,
                                LogLevel.INFO,
                                { str1 = recordedTask?.baseIntent?.component?.packageName },
                                { "State: Recording(taskPackage=$str1)" },
                                {
                                    str1 = hostPackage
                                    str2 = recordedTask?.baseIntent?.component?.packageName
                                },
                                { "State: Recording(hostPackage=$str1, taskPackage=$str2)" },
                            )
                            ScreenRecordChipModel.Recording(
                                hostPackage = hostPackage,
                                recordedTask = recordedTask,
                            )
                            ScreenRecordChipModel.Recording(recordedTask)
                        }
                    }
                }
Loading