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

Commit c2546fe4 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge changes from topic "caitlinshk-chip-screenshot" into main

* changes:
  [SB][Notif] Reduce log spam by adding `distinctUntilChanged`.
  [SB][Notif] Stable-sort notif chips by first appearance time.
  [SB][Notif] Update SBNotificationChipsInteractorTest to use Kosmos more.
  [SB][Notif] Remove demo commands for notification chips.
  [SB][Notif] Add margin_start dimen for ongoing activity chips.
parents 6084c696 96167e13
Loading
Loading
Loading
Loading
+0 −146
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel

import android.content.packageManager
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever

@SmallTest
class DemoNotifChipViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val commandRegistry = kosmos.commandRegistry
    private val pw = PrintWriter(StringWriter())

    private val underTest = kosmos.demoNotifChipViewModel

    @Before
    fun setUp() {
        underTest.start()
        whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
            .thenReturn(BitmapDrawable())
    }

    @Test
    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_flagOff_hidden() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            addDemoNotifChip()

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_noPackage_hidden() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_hasPackage_shown() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_hasText_shownWithText() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            commandRegistry.onShellCommand(
                pw,
                arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
            )

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_supportsColor() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            commandRegistry.onShellCommand(
                pw,
                arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"),
            )

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
            assertThat((latest as OngoingActivityChipModel.Shown).colors)
                .isInstanceOf(ColorsModel.Custom::class.java)
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun chip_hasHideArg_hidden() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chip)

            // First, show a chip
            addDemoNotifChip()
            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)

            // Then, hide the chip
            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))

            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
        }

    private fun addDemoNotifChip() {
        addDemoNotifChip(commandRegistry, pw)
    }

    companion object {
        fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
        }
    }
}
+23 −13
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                    promotedContent = PROMOTED_CONTENT,
                )

            val underTest = factory.create(startingNotif)
            val underTest = factory.create(startingNotif, creationTime = 1)

            val latest by collectLastValue(underTest.notificationChip)

@@ -71,7 +71,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = originalIconView,
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -99,7 +100,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = originalIconView,
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -127,7 +129,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = originalIconView,
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -153,7 +156,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = null,
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -171,7 +175,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = null,
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -189,7 +194,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                    statusBarChipIcon = mock(),
                    promotedContent = PROMOTED_CONTENT,
                )
            val underTest = factory.create(startingNotif)
            val underTest = factory.create(startingNotif, creationTime = 1)
            val latest by collectLastValue(underTest.notificationChip)
            assertThat(latest).isNotNull()

@@ -214,7 +219,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                    statusBarChipIcon = mock(),
                    promotedContent = PROMOTED_CONTENT,
                )
            val underTest = factory.create(startingNotif)
            val underTest = factory.create(startingNotif, creationTime = 1)
            val latest by collectLastValue(underTest.notificationChip)
            assertThat(latest).isNotNull()

@@ -239,7 +244,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        key = "notif1",
                        statusBarChipIcon = mock(),
                        promotedContent = null,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -259,7 +265,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        uid = UID,
                        statusBarChipIcon = mock(),
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -279,7 +286,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        uid = UID,
                        statusBarChipIcon = mock(),
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -297,7 +305,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        uid = UID,
                        statusBarChipIcon = mock(),
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )

            val latest by collectLastValue(underTest.notificationChip)
@@ -330,7 +339,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
                        uid = hiddenUid,
                        statusBarChipIcon = mock(),
                        promotedContent = PROMOTED_CONTENT,
                    )
                    ),
                    creationTime = 1,
                )
            val latest by collectLastValue(underTest.notificationChip)
            assertThat(latest).isNotNull()
+105 −17
Original line number Diff line number Diff line
@@ -23,7 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.collectValues
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
@@ -33,6 +36,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
@@ -43,17 +47,14 @@ import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val activeNotificationListRepository = kosmos.activeNotificationListRepository

    private val underTest by lazy {
        kosmos.statusBarNotificationChipsInteractor.also { it.start() }
    }
    private val Kosmos.underTest by
        Kosmos.Fixture { statusBarNotificationChipsInteractor.also { it.start() } }

    @Test
    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_flagOff_noNotifs() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            setNotifs(
@@ -72,7 +73,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_noNotifs_empty() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            setNotifs(emptyList())
@@ -83,7 +84,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_notifMissingStatusBarChipIconView_empty() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            setNotifs(
@@ -102,7 +103,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_onePromotedNotif_statusBarIconViewMatches() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            val icon = mock<StatusBarIconView>()
@@ -124,7 +125,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_onlyForPromotedNotifs() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            val firstIcon = mock<StatusBarIconView>()
@@ -159,7 +160,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_notifUpdatesGoThrough() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            val firstIcon = mock<StatusBarIconView>()
@@ -209,7 +210,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_promotedNotifDisappearsThenReappears() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            setNotifs(
@@ -248,10 +249,97 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
            assertThat(latest!![0].key).isEqualTo("notif")
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_sortedBasedOnFirstAppearanceTime() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            val firstIcon = mock<StatusBarIconView>()
            val secondIcon = mock<StatusBarIconView>()

            // First, add notif1 at t=1000
            fakeSystemClock.setCurrentTimeMillis(1000)
            val notif1 =
                activeNotificationModel(
                    key = "notif1",
                    statusBarChipIcon = firstIcon,
                    promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
                )
            setNotifs(listOf(notif1))
            assertThat(latest).hasSize(1)
            assertThat(latest!![0].key).isEqualTo("notif1")

            // WHEN we add notif2 at t=2000
            fakeSystemClock.advanceTime(1000)
            val notif2 =
                activeNotificationModel(
                    key = "notif2",
                    statusBarChipIcon = secondIcon,
                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
                )
            setNotifs(listOf(notif1, notif2))

            // THEN notif2 is ranked above notif1 because it appeared later
            assertThat(latest).hasSize(2)
            assertThat(latest!![0].key).isEqualTo("notif2")
            assertThat(latest!![1].key).isEqualTo("notif1")

            // WHEN notif1 and notif2 swap places
            setNotifs(listOf(notif2, notif1))

            // THEN notif2 is still ranked above notif1 to preserve chip ordering
            assertThat(latest).hasSize(2)
            assertThat(latest!![0].key).isEqualTo("notif2")
            assertThat(latest!![1].key).isEqualTo("notif1")

            // WHEN notif1 and notif2 swap places again
            setNotifs(listOf(notif1, notif2))

            // THEN notif2 is still ranked above notif1 to preserve chip ordering
            assertThat(latest).hasSize(2)
            assertThat(latest!![0].key).isEqualTo("notif2")
            assertThat(latest!![1].key).isEqualTo("notif1")

            // WHEN notif1 gets an update
            val notif1NewPromotedContent =
                PromotedNotificationContentModel.Builder("notif1").apply {
                    this.shortCriticalText = "Arrived"
                }
            setNotifs(
                listOf(
                    activeNotificationModel(
                        key = "notif1",
                        statusBarChipIcon = firstIcon,
                        promotedContent = notif1NewPromotedContent.build(),
                    ),
                    notif2,
                )
            )

            // THEN notif2 is still ranked above notif1 to preserve chip ordering
            assertThat(latest).hasSize(2)
            assertThat(latest!![0].key).isEqualTo("notif2")
            assertThat(latest!![1].key).isEqualTo("notif1")

            // WHEN notif1 disappears and then reappears
            fakeSystemClock.advanceTime(1000)
            setNotifs(listOf(notif2))
            assertThat(latest).hasSize(1)

            fakeSystemClock.advanceTime(1000)
            setNotifs(listOf(notif2, notif1))

            // THEN notif1 is now ranked first
            assertThat(latest).hasSize(2)
            assertThat(latest!![0].key).isEqualTo("notif1")
            assertThat(latest!![1].key).isEqualTo("notif2")
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun notificationChips_notifChangesKey() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectLastValue(underTest.notificationChips)

            val firstIcon = mock<StatusBarIconView>()
@@ -291,7 +379,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun onPromotedNotificationChipTapped_emitsKeys() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectValues(underTest.promotedNotificationChipTapEvent)

            underTest.onPromotedNotificationChipTapped("fakeKey")
@@ -308,7 +396,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun onPromotedNotificationChipTapped_sameKeyTwice_emitsTwice() =
        testScope.runTest {
        kosmos.runTest {
            val latest by collectValues(underTest.promotedNotificationChipTapEvent)

            underTest.onPromotedNotificationChipTapped("fakeKey")
@@ -319,7 +407,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
            assertThat(latest[1]).isEqualTo("fakeKey")
        }

    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
    private fun Kosmos.setNotifs(notifs: List<ActiveNotificationModel>) {
        activeNotificationListRepository.activeNotifications.value =
            ActiveNotificationsStore.Builder()
                .apply { notifs.forEach { addIndividualNotif(it) } }
+0 −2
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -97,7 +96,6 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
    @Before
    fun setUp() {
        setUpPackageManagerForMediaProjection(kosmos)
        kosmos.demoNotifChipViewModel.start()
        val icon =
            BitmapDrawable(
                context.resources,
+41 −37

File changed.

Preview size limit exceeded, changes collapsed.

Loading