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

Commit 6c6353db authored by Lyn Han's avatar Lyn Han Committed by Android (Google) Code Review
Browse files

Merge "Filter out seen apps from bundle header" into main

parents 70114adf 319ce8b5
Loading
Loading
Loading
Loading
+308 −15
Original line number Diff line number Diff line
@@ -22,11 +22,17 @@ import android.app.NotificationChannel.PROMOTIONS_ID
import android.app.NotificationChannel.RECS_ID
import android.app.NotificationChannel.SOCIAL_MEDIA_ID
import android.os.UserHandle
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.util.Pair
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MotionScheme
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.currentValue
import com.android.systemui.notifications.ui.composable.row.BundleHeader
import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.InternalNotificationsApi
@@ -36,6 +42,9 @@ import com.android.systemui.statusbar.notification.collection.render.BundleBarn
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.row.data.model.AppData
import com.android.systemui.statusbar.notification.row.data.repository.TEST_BUNDLE_SPEC
import com.android.systemui.statusbar.notification.row.data.repository.TEST_BUNDLE_SPEC_2
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.util.time.SystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertEquals
import kotlinx.coroutines.test.runTest
@@ -43,8 +52,10 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalMaterial3ExpressiveApi::class, InternalNotificationsApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@@ -54,6 +65,7 @@ class BundleCoordinatorTest : SysuiTestCase() {
    @Mock private lateinit var recsController: NodeController
    @Mock private lateinit var promoController: NodeController
    @Mock private lateinit var bundleBarn: BundleBarn
    @Mock private lateinit var systemClock: SystemClock

    private lateinit var coordinator: BundleCoordinator

@@ -73,6 +85,7 @@ class BundleCoordinatorTest : SysuiTestCase() {
                recsController,
                promoController,
                bundleBarn,
                systemClock,
            )
    }

@@ -117,23 +130,154 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertEquals(coordinator.bundler.getBundleIdOrNull(unclassifiedEntry), null)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun testUpdateAppData_emptyChildren_setsEmptyAppList() = runTest {
    fun membershipUpdater_notif_setKeyAndTime() {
        val testTime = 1000L
        whenever(systemClock.uptimeMillis()).thenReturn(testTime)

        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        val notifEntry = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        bundle.addChild(notifEntry)

        coordinator.bundleMembershipUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(notifEntry.timeAddedToBundle.first).isEqualTo(bundle.key)
        assertThat(notifEntry.timeAddedToBundle.second).isEqualTo(testTime)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun membershipUpdater_group_setKeyAndTime() {
        val testTime = 2000L
        whenever(systemClock.uptimeMillis()).thenReturn(testTime)

        val bundle = BundleEntry(TEST_BUNDLE_SPEC)

        val group1 = GroupEntry("key", 0L)
        val groupChild = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        groupChild.timeAddedToBundle = Pair(bundle.key, testTime)
        group1.rawChildren.add(groupChild)

        val groupSummary =
            NotificationEntryBuilder()
                .setPkg(pkg2)
                .setUser(user2)
                .setGroupSummary(context, true)
                .build()
        group1.summary = groupSummary

        bundle.addChild(group1)

        coordinator.bundleMembershipUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(groupSummary.timeAddedToBundle.first).isEqualTo(bundle.key)
        assertThat(groupSummary.timeAddedToBundle.second).isEqualTo(testTime)
        assertThat(groupChild.timeAddedToBundle.first).isEqualTo(bundle.key)
        assertThat(groupChild.timeAddedToBundle.second).isEqualTo(testTime)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun membershipUpdater_notifStaysInSameBundle_nonZeroTimeNotUpdated() {
        val initialTime = 100L
        val currentTime = 200L
        whenever(systemClock.uptimeMillis()).thenReturn(currentTime)

        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        val notif = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        bundle.addChild(notif)
        notif.timeAddedToBundle = Pair(bundle.key, initialTime) // Pre-set time

        coordinator.bundleMembershipUpdater.onBeforeRenderList(listOf(bundle))

        // Timestamp should not change if key is same and time was already > 0L
        assertThat(notif.timeAddedToBundle.first).isEqualTo(bundle.key)
        assertThat(notif.timeAddedToBundle.second).isEqualTo(initialTime)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun membershipUpdater_notifStaysInSameBundle_zeroTimeUpdated() {
        val currentTime = 200L
        whenever(systemClock.uptimeMillis()).thenReturn(currentTime)

        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        val notif = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        bundle.addChild(notif)
        notif.timeAddedToBundle = Pair(bundle.key, 0L)

        coordinator.bundleMembershipUpdater.onBeforeRenderList(listOf(bundle))

        // Timestamp should update because it was 0L
        assertThat(notif.timeAddedToBundle.first).isEqualTo(bundle.key)
        assertThat(notif.timeAddedToBundle.second).isEqualTo(currentTime)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun membershipUpdater_entryMovesToNewBundle_updatesKeyAndTime() {
        val time1 = 100L
        val time2 = 200L
        whenever(systemClock.uptimeMillis()).thenReturn(time2)

        val bundleOld = BundleEntry(TEST_BUNDLE_SPEC)
        val bundleNew = BundleEntry(TEST_BUNDLE_SPEC_2)

        val notif = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif.timeAddedToBundle = Pair(bundleOld.key, time1)

        bundleNew.addChild(notif)
        coordinator.bundleMembershipUpdater.onBeforeRenderList(listOf(bundleNew))

        assertThat(notif.timeAddedToBundle.first).isEqualTo(bundleNew.key)
        assertThat(notif.timeAddedToBundle.second).isEqualTo(time2)
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_emptyChildren_setsEmptyAppListWhenCollapsed() = runTest {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )
        assertThat(bundle.children).isEmpty()

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(currentValue(bundle.bundleRepository.appDataList)).isEmpty()
    }

    @OptIn(InternalNotificationsApi::class)
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_emptyChildren_setsEmptyAppListWhenExpanded() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(BundleHeader.Scenes.Expanded, MotionScheme.standard())
        assertThat(bundle.children).isEmpty()

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))
        assertThat(bundle.bundleRepository.appDataList.value).isEmpty()
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun testUpdateAppData_twoNotifs() = runTest {
    fun appDataUpdater_twoNotifs_whileCollapsed() = runTest {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )
        val time1 = 100L
        val time2 = 200L

        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, time1)

        val notif2 = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        notif2.timeAddedToBundle = Pair(bundle.key, time2)

        bundle.addChild(notif1)
        bundle.addChild(notif2)
@@ -141,18 +285,29 @@ class BundleCoordinatorTest : SysuiTestCase() {
        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(currentValue(bundle.bundleRepository.appDataList))
            .containsExactly(AppData(pkg1, user1), AppData(pkg2, user2))
            .containsExactly(AppData(pkg1, user1, time1), AppData(pkg2, user2, time2))
    }

    @OptIn(InternalNotificationsApi::class)
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun testUpdateAppData_notifAndGroup() = runTest {
    fun appDataUpdater_notifAndGroup_whileCollapsed() = runTest {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )

        val time1 = 100L
        val time2 = 200L
        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, time1)

        val group1 = GroupEntry("key", 0L)
        val groupChild = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        groupChild.timeAddedToBundle = Pair(bundle.key, time2)
        group1.rawChildren.add(groupChild)

        val groupSummary =
            NotificationEntryBuilder()
                .setPkg(pkg2)
@@ -167,7 +322,152 @@ class BundleCoordinatorTest : SysuiTestCase() {
        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(currentValue(bundle.bundleRepository.appDataList))
            .containsExactly(AppData(pkg1, user1), AppData(pkg2, user2))
            .containsExactly(AppData(pkg1, user1, time1), AppData(pkg2, user2, time2))
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_notifAndGroup_usesMaxTimeFromSummaryOrChildren() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )

        val timeNotif1 = 100L
        val timeGroupChild = 150L
        val timeGroupSummary = 200L

        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, timeNotif1)

        val group1 = GroupEntry("key", 0L)
        val groupSummary =
            NotificationEntryBuilder()
                .setPkg(pkg2)
                .setUser(user2)
                .setGroupSummary(context, true)
                .build()
        groupSummary.timeAddedToBundle = Pair(bundle.key, timeGroupSummary)
        group1.summary = groupSummary

        val groupChild = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        groupChild.timeAddedToBundle = Pair(bundle.key, timeGroupChild)
        group1.rawChildren.add(groupChild)

        bundle.addChild(notif1)
        bundle.addChild(group1)

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        // 1. For pkg1, user1: AppData(pkg1, user1, timeNotif1)
        // 2. For pkg2, user2 (from group):
        //    - Summary: AppData(pkg2, user2, timeGroupSummary=200L)
        //    - Child:   AppData(pkg2, user2, timeGroupChild=150L)
        //    The `maxByOrNull` will pick the one with timeGroupSummary (200L)
        assertThat(bundle.bundleRepository.appDataList.value)
            .containsExactly(
                AppData(pkg1, user1, timeNotif1),
                AppData(pkg2, user2, timeGroupSummary),
            )
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_twoNotifsWhileCollapsed_emptyAfterExpand() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(BundleHeader.Scenes.Expanded, MotionScheme.standard())

        val time1 = 100L
        val time2 = 200L

        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, time1)

        val notif2 = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        notif2.timeAddedToBundle = Pair(bundle.key, time2)

        bundle.addChild(notif1)
        bundle.addChild(notif2)

        bundle.bundleRepository.appDataList.value = listOf(AppData(pkg1, user1, time1))

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))
        assertThat(bundle.bundleRepository.appDataList.value).isEmpty()
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_closedBundle_usesMaxTimeAddedToBundleForSameAppUser() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )

        val time1 = 1000L
        val time2 = 2000L // Latest time, should be chosen
        val time3 = 500L

        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, time1)

        val notif2 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif2.timeAddedToBundle = Pair(bundle.key, time2)

        val notif3 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif3.timeAddedToBundle = Pair(bundle.key, time3)

        val notifOtherApp = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        notifOtherApp.timeAddedToBundle = Pair(bundle.key, time1)

        bundle.addChild(notif1)
        bundle.addChild(notif2)
        bundle.addChild(notif3)
        bundle.addChild(notifOtherApp)

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        // Expected processing:
        // 1. Collect AppData for notif1, notif2, notif3, notifOtherApp
        // 2. Filter by time > 0L (all pass).
        // 3. Group by (pkg, user).
        //    - Group (pkg1, user1) will have AppData with times T1, T2, T3.
        //    - Group (pkg2, user2) will have AppData with time T1.
        // 4. mapNotNull with maxByOrNull on timeAddedToBundle for each group.
        //    - For (pkg1, user1), max is T2.
        //    - For (pkg2, user2), max is T1.
        assertThat(bundle.bundleRepository.appDataList.value)
            .containsExactly(AppData(pkg1, user1, time2), AppData(pkg2, user2, time1))
    }

    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    @Test
    fun appDataUpdater_filtersOutZeroTimeAddedToBundleInFinalList() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
        bundle.bundleRepository.state =
            MutableSceneTransitionLayoutState(
                BundleHeader.Scenes.Collapsed,
                MotionScheme.standard(),
            )
        val validTime = 3000L

        val notif1 = NotificationEntryBuilder().setPkg(pkg1).setUser(user1).build()
        notif1.timeAddedToBundle = Pair(bundle.key, 0L)

        val notif2 = NotificationEntryBuilder().setPkg(pkg2).setUser(user2).build()
        notif2.timeAddedToBundle = Pair(bundle.key, validTime)

        bundle.addChild(notif1)
        bundle.addChild(notif2)

        coordinator.bundleAppDataUpdater.onBeforeRenderList(listOf(bundle))

        assertThat(bundle.bundleRepository.appDataList.value)
            .containsExactly(AppData(pkg2, user2, validTime))
    }

    @Test
@@ -177,7 +477,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(0)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_addNotif() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -187,7 +486,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(1)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_addGroup() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -200,7 +498,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(2)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_addMultipleGroups() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -218,7 +515,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(3)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_addNotifAndGroup() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -232,7 +528,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(2)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_removeNotif() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -253,7 +548,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(1)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_removeGroupChild() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
@@ -270,7 +564,6 @@ class BundleCoordinatorTest : SysuiTestCase() {
        assertThat(bundle.bundleRepository.numberOfChildren).isEqualTo(1)
    }

    @OptIn(InternalNotificationsApi::class)
    @Test
    fun testTotalCount_clearChildren() {
        val bundle = BundleEntry(TEST_BUNDLE_SPEC)
+153 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
package com.android.systemui.statusbar.notification.row.domain.interactor

import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.platform.test.annotations.EnableFlags
@@ -49,10 +50,12 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import platform.test.motion.compose.runMonotonicClockTest
@@ -72,6 +75,10 @@ class BundleInteractorTest : SysuiTestCase() {

    private lateinit var underTest: BundleInteractor

    private val drawable1: Drawable = ColorDrawable(Color.RED)
    private val drawable2: Drawable = ColorDrawable(Color.GREEN)
    private val drawable3: Drawable = ColorDrawable(Color.BLUE)

    @Before
    fun setUp() {
        kosmos.appIconProvider = kosmos.mockAppIconProvider
@@ -89,9 +96,6 @@ class BundleInteractorTest : SysuiTestCase() {
                    }
                }

            val drawable1: Drawable = ColorDrawable(android.graphics.Color.RED)
            val drawable2: Drawable = ColorDrawable(android.graphics.Color.GREEN)
            val drawable3: Drawable = ColorDrawable(android.graphics.Color.BLUE)
            val unusedDrawable: Drawable = ColorDrawable(android.graphics.Color.YELLOW)

            whenever(
@@ -121,6 +125,152 @@ class BundleInteractorTest : SysuiTestCase() {
            assertThat(result).containsExactly(drawable1, drawable2, drawable3).inOrder()
        }

    @Test
    fun previewIcons_collapseTimeZero_fetchAll() =
        testScope.runTest {
            // Arrange
            val app1Data =
                mock<AppData> {
                    on { packageName }.thenReturn("app1")
                    on { timeAddedToBundle }.thenReturn(10L)
                }
            val app2Data =
                mock<AppData> {
                    on { packageName }.thenReturn("app2")
                    on { timeAddedToBundle }.thenReturn(20L)
                }
            val appDataList = listOf(app1Data, app2Data)

            whenever(
                    kosmos.mockAppIconProvider.getOrFetchAppIcon(
                        eq("app1"),
                        anyOrNull<Context>(),
                        eq(false),
                        eq(false),
                    )
                )
                .thenReturn(drawable1)
            whenever(
                    kosmos.mockAppIconProvider.getOrFetchAppIcon(
                        eq("app2"),
                        anyOrNull<Context>(),
                        eq(false),
                        eq(false),
                    )
                )
                .thenReturn(drawable2)

            testBundleRepository.lastCollapseTime = 0L
            testBundleRepository.appDataList.value = appDataList
            runCurrent()

            // Act
            val result = underTest.previewIcons.first()

            // Assert
            assertThat(result).containsExactly(drawable1, drawable2).inOrder()
            verify(kosmos.mockAppIconProvider)
                .getOrFetchAppIcon(eq("app1"), anyOrNull<Context>(), eq(false), eq(false))
            verify(kosmos.mockAppIconProvider)
                .getOrFetchAppIcon(eq("app2"), anyOrNull<Context>(), eq(false), eq(false))
        }

    @Test
    fun previewIcons_collapseTimeNonZero_filterBytime() =
        testScope.runTest {
            // Arrange
            val collapseTime = 100L
            val appDataOld =
                mock<AppData> {
                    on { packageName }.thenReturn("old_app")
                    on { timeAddedToBundle }.thenReturn(50L) // Older than collapseTime
                }
            val appDataNew =
                mock<AppData> {
                    on { packageName }.thenReturn("new_app")
                    on { timeAddedToBundle }.thenReturn(150L) // Newer than collapseTime
                }
            val appDataAtCollapse =
                mock<AppData> {
                    on { packageName }.thenReturn("at_collapse_app")
                    on { timeAddedToBundle }
                        .thenReturn(collapseTime) // Equal to collapseTime (should be filtered out)
                }
            val appDataList = listOf(appDataOld, appDataNew, appDataAtCollapse)

            whenever(
                    kosmos.mockAppIconProvider.getOrFetchAppIcon(
                        eq("new_app"),
                        anyOrNull<Context>(),
                        eq(false),
                        eq(false),
                    )
                )
                .thenReturn(drawable3)

            testBundleRepository.lastCollapseTime = collapseTime
            testBundleRepository.appDataList.value = appDataList
            runCurrent()

            // Act
            val result = underTest.previewIcons.first()

            // Assert
            assertThat(result).containsExactly(drawable3)
            verify(kosmos.mockAppIconProvider, times(0))
                .getOrFetchAppIcon(eq("old_app"), anyOrNull<Context>(), eq(false), eq(false))
            verify(kosmos.mockAppIconProvider, times(0))
                .getOrFetchAppIcon(
                    eq("at_collapse_app"),
                    anyOrNull<Context>(),
                    eq(false),
                    eq(false),
                )
            verify(kosmos.mockAppIconProvider)
                .getOrFetchAppIcon(eq("new_app"), anyOrNull<Context>(), eq(false), eq(false))
        }

    @Test
    fun previewIcons_allAppDataOlderThanCollapseTime_emitsEmptyList() =
        testScope.runTest {
            // Arrange
            val collapseTime = 200L
            val appDataList =
                List(3) { i ->
                    mock<AppData> {
                        on { packageName }.thenReturn("app$i")
                        on { timeAddedToBundle }.thenReturn(i * 1L)
                    }
                }

            testBundleRepository.lastCollapseTime = collapseTime
            testBundleRepository.appDataList.value = appDataList
            runCurrent()

            // Act
            val result = underTest.previewIcons.first()

            // Assert
            assertThat(result).isEmpty()
            verify(kosmos.mockAppIconProvider, times(0))
                .getOrFetchAppIcon(anyString(), anyOrNull<Context>(), eq(false), eq(false))
        }

    @Test
    fun previewIcons_emptyAppDataList_emitsEmptyIconList() =
        testScope.runTest {
            // Arrange
            testBundleRepository.appDataList.value = emptyList()
            testBundleRepository.lastCollapseTime = 0L
            runCurrent()

            // Act
            val result = underTest.previewIcons.first()

            // Assert
            assertThat(result).isEmpty()
        }

    @Test
    fun setExpansionState_sets_state_to_expanded() = runMonotonicClockTest {
        // Arrange
+3 −0
Original line number Diff line number Diff line
@@ -274,6 +274,9 @@ public class NotificationGroupingUtil {
     * Reset the modifications to this row for removing it from the group.
     */
    public void restoreChildNotification(ExpandableNotificationRow row) {
        if (row.getNotificationViewWrapper() == null) {
            return;
        }
        for (int compI = 0; compI < mProcessors.size(); compI++) {
            mProcessors.get(compI).apply(row, true /* reset */);
        }
+32 −0

File changed.

Preview size limit exceeded, changes collapsed.

+121 −28

File changed.

Preview size limit exceeded, changes collapsed.

Loading