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

Commit 9e008283 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Bundles views: adapt ENR creation and group classes

Converts 'rendering' group management classes and ExpandableNotificationRow
inflation to use entry adapters rather than NotificationEntry directly.

Additionally updates a couple of places in the pipeline to use pipeline data
structures directly rather than wandering into the 'render' package

Test: StatusBarRemoteInputCallbackTest
Test: PeopleNotificationIdentifierTest
Test: ExpandableNotificationRowTest
Test: GroupExpansionManagerTest
Test: GroupMembershipManagerTest
Test: DynamicChildBindControllerTest
Test: HighPriorityProviderTest
Test: NotificationEntryTest
Test: NotificationBundleTest
Bug: 395857098
Flag: com.android.systemui.notification_bundle_ui

Change-Id: I9d629cffca44277f06bd1676e46106b43e2bd85a
parent 7c34810b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -51,8 +51,10 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.people.NotificationPersonExtractor;
import com.android.systemui.util.DeviceConfigProxyFake;

import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+1 −1
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ public class DynamicChildBindControllerTest extends SysuiTestCase {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        inflater.setFactory2(
                new RowInflaterTask.RowAsyncLayoutInflater(entry, new FakeSystemClock(), mock(
                        RowInflaterTaskLogger.class)));
                        RowInflaterTaskLogger.class), mContext.getUser()));

        ExpandableNotificationRow row = (ExpandableNotificationRow)
                inflater.inflate(R.layout.status_bar_notification_row, null);
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.notification.collection

import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BundleEntryTest : SysuiTestCase() {
    private lateinit var underTest: BundleEntry

    @get:Rule
    val setFlagsRule = SetFlagsRule()

    @Before
    fun setUp() {
        underTest = BundleEntry("key")
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun getParent_adapter() {
        assertThat(underTest.entryAdapter.parent).isEqualTo(GroupEntry.ROOT_ENTRY)
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun isTopLevelEntry_adapter() {
        assertThat(underTest.entryAdapter.isTopLevelEntry).isTrue()
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun getRow_adapter() {
        assertThat(underTest.entryAdapter.row).isNull()
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun getGroupRoot_adapter() {
        assertThat(underTest.entryAdapter.groupRoot).isEqualTo(underTest.entryAdapter)
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun getKey_adapter() {
        assertThat(underTest.entryAdapter.key).isEqualTo("key")
    }
}
 No newline at end of file
+15 −7
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ import static org.mockito.Mockito.when;

import android.app.Notification;
import android.app.NotificationChannel;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -39,8 +42,10 @@ import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -57,6 +62,9 @@ public class HighPriorityProviderTest extends SysuiTestCase {
    @Mock private GroupMembershipManager mGroupMembershipManager;
    private HighPriorityProvider mHighPriorityProvider;

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
@@ -210,6 +218,7 @@ public class HighPriorityProviderTest extends SysuiTestCase {
    }

    @Test
    @DisableFlags(NotificationBundleUi.FLAG_NAME)
    public void testIsHighPriority_checkChildrenToCalculatePriority_legacy() {
        // GIVEN: a summary with low priority has a highPriorityChild and a lowPriorityChild
        final NotificationEntry summary = createNotifEntry(false);
@@ -247,20 +256,18 @@ public class HighPriorityProviderTest extends SysuiTestCase {
    }

    @Test
    public void testIsHighPriority_checkChildrenToCalculatePriority() {
    public void testIsHighPriority_checkChildrenViewsToCalculatePriority() {
        // GIVEN:
        // parent with summary = lowPrioritySummary
        //      NotificationEntry = lowPriorityChild
        //      NotificationEntry = highPriorityChild
        List<NotificationEntry> children = List.of(createNotifEntry(false), createNotifEntry(true));
        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
        final GroupEntry parentEntry = new GroupEntryBuilder()
                .setSummary(lowPrioritySummary)
                .setChildren(children)
                .build();
        when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn(
                new ArrayList<>(
                        List.of(
                                createNotifEntry(false),
                                createNotifEntry(true))));
        when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn(children);

        // THEN the GroupEntry parentEntry is high priority since it has a high priority child
        assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
@@ -272,10 +279,11 @@ public class HighPriorityProviderTest extends SysuiTestCase {
        // parent with summary = lowPrioritySummary
        //      NotificationEntry = lowPriorityChild
        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
        final NotificationEntry lowPriorityChild = createNotifEntry(false);
        final GroupEntry parentEntry = new GroupEntryBuilder()
                .setSummary(lowPrioritySummary)
                .setChildren(List.of(lowPriorityChild))
                .build();
        final NotificationEntry lowPriorityChild = createNotifEntry(false);
        when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn(
                new ArrayList<>(List.of(lowPriorityChild)));

+80 −2
Original line number Diff line number Diff line
@@ -17,23 +17,29 @@
package com.android.systemui.statusbar.notification.collection.render

import android.os.Build
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.assertLogsWtf
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
@@ -44,6 +50,9 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class GroupExpansionManagerTest : SysuiTestCase() {
    @get:Rule
    val setFlagsRule = SetFlagsRule()

    private lateinit var underTest: GroupExpansionManagerImpl

    private val dumpManager: DumpManager = mock()
@@ -52,8 +61,8 @@ class GroupExpansionManagerTest : SysuiTestCase() {
    private val pipeline: NotifPipeline = mock()
    private lateinit var beforeRenderListListener: OnBeforeRenderListListener

    private val summary1 = notificationEntry("foo", 1)
    private val summary2 = notificationEntry("bar", 1)
    private val summary1 = notificationSummaryEntry("foo", 1)
    private val summary2 = notificationSummaryEntry("bar", 1)
    private val entries =
        listOf<ListEntry>(
            GroupEntryBuilder()
@@ -82,15 +91,25 @@ class GroupExpansionManagerTest : SysuiTestCase() {
    private fun notificationEntry(pkg: String, id: Int) =
        NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() }

    private fun notificationSummaryEntry(pkg: String, id: Int) =
        NotificationEntryBuilder().setPkg(pkg).setId(id).setParent(GroupEntry.ROOT_ENTRY).build()
            .apply { row = mock() }

    @Before
    fun setUp() {
        whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
        whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)

        whenever(groupMembershipManager.getGroupRoot(summary1.entryAdapter))
            .thenReturn(summary1.entryAdapter)
        whenever(groupMembershipManager.getGroupRoot(summary2.entryAdapter))
            .thenReturn(summary2.entryAdapter)

        underTest = GroupExpansionManagerImpl(dumpManager, groupMembershipManager)
    }

    @Test
    @DisableFlags(NotificationBundleUi.FLAG_NAME)
    fun notifyOnlyOnChange() {
        var listenerCalledCount = 0
        underTest.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
@@ -108,6 +127,25 @@ class GroupExpansionManagerTest : SysuiTestCase() {
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun notifyOnlyOnChange_withEntryAdapter() {
        var listenerCalledCount = 0
        underTest.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }

        underTest.setGroupExpanded(summary1.entryAdapter, false)
        assertThat(listenerCalledCount).isEqualTo(0)
        underTest.setGroupExpanded(summary1.entryAdapter, true)
        assertThat(listenerCalledCount).isEqualTo(1)
        underTest.setGroupExpanded(summary2.entryAdapter, true)
        assertThat(listenerCalledCount).isEqualTo(2)
        underTest.setGroupExpanded(summary1.entryAdapter, true)
        assertThat(listenerCalledCount).isEqualTo(2)
        underTest.setGroupExpanded(summary2.entryAdapter, false)
        assertThat(listenerCalledCount).isEqualTo(3)
    }

    @Test
    @DisableFlags(NotificationBundleUi.FLAG_NAME)
    fun expandUnattachedEntry() {
        // First, expand the entry when it is attached.
        underTest.setGroupExpanded(summary1, true)
@@ -122,6 +160,22 @@ class GroupExpansionManagerTest : SysuiTestCase() {
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun expandUnattachedEntryAdapter() {
        // First, expand the entry when it is attached.
        underTest.setGroupExpanded(summary1.entryAdapter, true)
        assertThat(underTest.isGroupExpanded(summary1.entryAdapter)).isTrue()

        // Un-attach it, and un-expand it.
        NotificationEntryBuilder.setNewParent(summary1, null)
        underTest.setGroupExpanded(summary1.entryAdapter, false)

        // Expanding again should throw.
        assertLogsWtf { underTest.setGroupExpanded(summary1.entryAdapter, true) }
    }

    @Test
    @DisableFlags(NotificationBundleUi.FLAG_NAME)
    fun syncWithPipeline() {
        underTest.attach(pipeline)
        beforeRenderListListener = withArgCaptor {
@@ -143,4 +197,28 @@ class GroupExpansionManagerTest : SysuiTestCase() {
        verify(listener).onGroupExpansionChange(summary1.row, false)
        verifyNoMoreInteractions(listener)
    }

    @Test
    @EnableFlags(NotificationBundleUi.FLAG_NAME)
    fun syncWithPipeline_withEntryAdapter() {
        underTest.attach(pipeline)
        beforeRenderListListener = withArgCaptor {
            verify(pipeline).addOnBeforeRenderListListener(capture())
        }

        val listener: OnGroupExpansionChangeListener = mock()
        underTest.registerGroupExpansionChangeListener(listener)

        beforeRenderListListener.onBeforeRenderList(entries)
        verify(listener, never()).onGroupExpansionChange(any(), any())

        // Expand one of the groups.
        underTest.setGroupExpanded(summary1.entryAdapter, true)
        verify(listener).onGroupExpansionChange(summary1.row, true)

        // Empty the pipeline list and verify that the group is no longer expanded.
        beforeRenderListListener.onBeforeRenderList(emptyList())
        verify(listener).onGroupExpansionChange(summary1.row, false)
        verifyNoMoreInteractions(listener)
    }
}
Loading