Enabled x button for clearable silent Notif[s]
Fixes: 204127880 Bug: 199902183 Test: RankingCoordinatorTest ShadeListBuilderTest Merged-In: If55b0b7fc35bb6ece83878cf3db0d7abfc2a214a Change-Id: If55b0b7fc35bb6ece83878cf3db0d7abfc2a214a
This commit is contained in:
@@ -36,7 +36,7 @@ public abstract class ListEntry {
|
||||
private final ListAttachState mPreviousAttachState = ListAttachState.create();
|
||||
private final ListAttachState mAttachState = ListAttachState.create();
|
||||
|
||||
ListEntry(String key, long creationTime) {
|
||||
protected ListEntry(String key, long creationTime) {
|
||||
mKey = key;
|
||||
mCreationTime = creationTime;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@ public class ShadeListBuilder implements Dumpable {
|
||||
private final SystemClock mSystemClock;
|
||||
private final ShadeListBuilderLogger mLogger;
|
||||
private final NotificationInteractionTracker mInteractionTracker;
|
||||
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
|
||||
private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
|
||||
|
||||
private List<ListEntry> mNotifList = new ArrayList<>();
|
||||
private List<ListEntry> mNewNotifList = new ArrayList<>();
|
||||
@@ -356,7 +358,7 @@ public class ShadeListBuilder implements Dumpable {
|
||||
// section by our list of custom comparators
|
||||
dispatchOnBeforeSort(mReadOnlyNotifList);
|
||||
mPipelineState.incrementTo(STATE_SORTING);
|
||||
sortList();
|
||||
sortListAndNotifySections();
|
||||
|
||||
// Step 7: Lock in our group structure and log anything that's changed since the last run
|
||||
mPipelineState.incrementTo(STATE_FINALIZING);
|
||||
@@ -382,6 +384,22 @@ public class ShadeListBuilder implements Dumpable {
|
||||
mIterationCount++;
|
||||
}
|
||||
|
||||
private void notifySectionEntriesUpdated() {
|
||||
NotifSection currentSection = null;
|
||||
mTempSectionMembers.clear();
|
||||
for (int i = 0; i < mNotifList.size(); i++) {
|
||||
ListEntry currentEntry = mNotifList.get(i);
|
||||
if (currentSection != currentEntry.getSection()) {
|
||||
if (currentSection != null) {
|
||||
currentSection.getSectioner().onEntriesUpdated(mTempSectionMembers);
|
||||
mTempSectionMembers.clear();
|
||||
}
|
||||
currentSection = currentEntry.getSection();
|
||||
}
|
||||
mTempSectionMembers.add(currentEntry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Points mNotifList to the list stored in mNewNotifList.
|
||||
* Reuses the (emptied) mNotifList as mNewNotifList.
|
||||
@@ -713,7 +731,7 @@ public class ShadeListBuilder implements Dumpable {
|
||||
}
|
||||
}
|
||||
|
||||
private void sortList() {
|
||||
private void sortListAndNotifySections() {
|
||||
// Assign sections to top-level elements and sort their children
|
||||
for (ListEntry entry : mNotifList) {
|
||||
NotifSection section = applySections(entry);
|
||||
@@ -728,6 +746,9 @@ public class ShadeListBuilder implements Dumpable {
|
||||
|
||||
// Finally, sort all top-level elements
|
||||
mNotifList.sort(mTopLevelComparator);
|
||||
|
||||
// notify sections since the list is sorted now
|
||||
notifySectionEntriesUpdated();
|
||||
}
|
||||
|
||||
private void freeEmptyGroups() {
|
||||
@@ -937,7 +958,6 @@ public class ShadeListBuilder implements Dumpable {
|
||||
}
|
||||
|
||||
entry.getAttachState().setSection(finalSection);
|
||||
|
||||
return finalSection;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection.coordinator;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
@@ -27,9 +28,12 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController;
|
||||
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
|
||||
import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
|
||||
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -44,7 +48,8 @@ public class RankingCoordinator implements Coordinator {
|
||||
public static final boolean SHOW_ALL_SECTIONS = false;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final HighPriorityProvider mHighPriorityProvider;
|
||||
private final NodeController mSilentHeaderController;
|
||||
private final NodeController mSilentNodeController;
|
||||
private final SectionHeaderController mSilentHeaderController;
|
||||
private final NodeController mAlertingHeaderController;
|
||||
|
||||
@Inject
|
||||
@@ -52,10 +57,12 @@ public class RankingCoordinator implements Coordinator {
|
||||
StatusBarStateController statusBarStateController,
|
||||
HighPriorityProvider highPriorityProvider,
|
||||
@AlertingHeader NodeController alertingHeaderController,
|
||||
@SilentHeader NodeController silentHeaderController) {
|
||||
@SilentHeader SectionHeaderController silentHeaderController,
|
||||
@SilentHeader NodeController silentNodeController) {
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mHighPriorityProvider = highPriorityProvider;
|
||||
mAlertingHeaderController = alertingHeaderController;
|
||||
mSilentNodeController = silentNodeController;
|
||||
mSilentHeaderController = silentHeaderController;
|
||||
}
|
||||
|
||||
@@ -101,7 +108,19 @@ public class RankingCoordinator implements Coordinator {
|
||||
@Nullable
|
||||
@Override
|
||||
public NodeController getHeaderNodeController() {
|
||||
return mSilentHeaderController;
|
||||
return mSilentNodeController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
|
||||
mSilentHeaderController.setClearSectionEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mSilentHeaderController.setClearSectionEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeSpec;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}.
|
||||
*/
|
||||
@@ -46,4 +48,10 @@ public abstract class NotifSectioner extends Pluggable<NotifSectioner> {
|
||||
public @Nullable NodeController getHeaderNodeController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify of children of this section being updated
|
||||
* @param entries of this section that are borrowed (must clone to store)
|
||||
*/
|
||||
public void onEntriesUpdated(List<ListEntry> entries) {}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import javax.inject.Inject
|
||||
interface SectionHeaderController {
|
||||
fun reinflateView(parent: ViewGroup)
|
||||
val headerView: SectionHeaderView?
|
||||
fun setOnClearAllClickListener(listener: View.OnClickListener)
|
||||
fun setClearSectionEnabled(enabled: Boolean)
|
||||
fun setOnClearSectionClickListener(listener: View.OnClickListener)
|
||||
}
|
||||
|
||||
@SectionHeaderScope
|
||||
@@ -46,6 +47,7 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
|
||||
) : NodeController, SectionHeaderController {
|
||||
|
||||
private var _view: SectionHeaderView? = null
|
||||
private var clearAllButtonEnabled = false
|
||||
private var clearAllClickListener: View.OnClickListener? = null
|
||||
private val onHeaderClickListener = View.OnClickListener {
|
||||
activityStarter.startActivity(
|
||||
@@ -76,12 +78,18 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
|
||||
parent.addView(inflated, oldPos)
|
||||
}
|
||||
_view = inflated
|
||||
_view?.setClearSectionButtonEnabled(clearAllButtonEnabled)
|
||||
}
|
||||
|
||||
override val headerView: SectionHeaderView?
|
||||
get() = _view
|
||||
|
||||
override fun setOnClearAllClickListener(listener: View.OnClickListener) {
|
||||
override fun setClearSectionEnabled(enabled: Boolean) {
|
||||
clearAllButtonEnabled = enabled
|
||||
_view?.setClearSectionButtonEnabled(enabled)
|
||||
}
|
||||
|
||||
override fun setOnClearSectionClickListener(listener: View.OnClickListener) {
|
||||
clearAllClickListener = listener
|
||||
_view?.setOnClearAllClickListener(listener)
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ class NotificationSectionsManager @Inject internal constructor(
|
||||
silentHeaderView?.run {
|
||||
val hasActiveClearableNotifications = this@NotificationSectionsManager.parent
|
||||
.hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
|
||||
setAreThereDismissableGentleNotifs(hasActiveClearableNotifications)
|
||||
setClearSectionButtonEnabled(hasActiveClearableNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -813,7 +813,7 @@ public class NotificationStackScrollLayoutController {
|
||||
mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
|
||||
}
|
||||
mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
|
||||
mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
|
||||
mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications());
|
||||
}
|
||||
|
||||
private boolean isInVisibleLocation(NotificationEntry entry) {
|
||||
|
||||
@@ -85,8 +85,12 @@ public class SectionHeaderView extends StackScrollerDecorView {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setAreThereDismissableGentleNotifs(boolean areThereDismissableGentleNotifs) {
|
||||
mClearAllButton.setVisibility(areThereDismissableGentleNotifs ? View.VISIBLE : View.GONE);
|
||||
/**
|
||||
* Show the clear section [X] button
|
||||
* @param enabled
|
||||
*/
|
||||
public void setClearSectionButtonEnabled(boolean enabled) {
|
||||
mClearAllButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -42,6 +43,7 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
@@ -78,6 +80,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SmallTest
|
||||
@@ -607,6 +610,30 @@ public class ShadeListBuilderTest extends SysuiTestCase {
|
||||
assertEquals(promoter2, mEntrySet.get(3).getNotifPromoter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifSectionsChildrenUpdated() {
|
||||
AtomicBoolean validChildren = new AtomicBoolean(false);
|
||||
final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
|
||||
@Nullable
|
||||
@Override
|
||||
public void onEntriesUpdated(List<ListEntry> entries) {
|
||||
super.onEntriesUpdated(entries);
|
||||
validChildren.set(entries.size() == 2);
|
||||
}
|
||||
});
|
||||
mListBuilder.setSectioners(Arrays.asList(pkg1Sectioner));
|
||||
|
||||
addNotif(0, PACKAGE_4);
|
||||
addNotif(1, PACKAGE_1);
|
||||
addNotif(2, PACKAGE_1);
|
||||
addNotif(3, PACKAGE_3);
|
||||
|
||||
dispatchBuild();
|
||||
|
||||
verify(pkg1Sectioner, times(1)).onEntriesUpdated(any());
|
||||
assertTrue(validChildren.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifSections() {
|
||||
// GIVEN a filter that removes all PACKAGE_4 notifs and sections that divide
|
||||
|
||||
@@ -21,17 +21,22 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.RankingBuilder;
|
||||
import com.android.systemui.statusbar.notification.collection.ListEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
@@ -39,6 +44,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController;
|
||||
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -46,8 +52,11 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class RankingCoordinatorTest extends SysuiTestCase {
|
||||
@@ -56,7 +65,8 @@ public class RankingCoordinatorTest extends SysuiTestCase {
|
||||
@Mock private HighPriorityProvider mHighPriorityProvider;
|
||||
@Mock private NotifPipeline mNotifPipeline;
|
||||
@Mock private NodeController mAlertingHeaderController;
|
||||
@Mock private NodeController mSilentHeaderController;
|
||||
@Mock private NodeController mSilentNodeController;
|
||||
@Mock private SectionHeaderController mSilentHeaderController;
|
||||
|
||||
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
|
||||
|
||||
@@ -72,7 +82,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
RankingCoordinator rankingCoordinator = new RankingCoordinator(
|
||||
mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
|
||||
mSilentHeaderController);
|
||||
mSilentHeaderController, mSilentNodeController);
|
||||
mEntry = new NotificationEntryBuilder().build();
|
||||
|
||||
rankingCoordinator.attach(mNotifPipeline);
|
||||
@@ -84,6 +94,28 @@ public class RankingCoordinatorTest extends SysuiTestCase {
|
||||
mSilentSectioner = rankingCoordinator.getSilentSectioner();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSilentHeaderClearableChildrenUpdate() {
|
||||
StatusBarNotification sbn = Mockito.mock(StatusBarNotification.class);
|
||||
Mockito.doReturn("key").when(sbn).getKey();
|
||||
Mockito.doReturn(Mockito.mock(Notification.class)).when(sbn).getNotification();
|
||||
NotificationEntry entry = new NotificationEntryBuilder().setSbn(sbn).build();
|
||||
ListEntry listEntry = new ListEntry("key", 0L) {
|
||||
@Nullable
|
||||
@Override
|
||||
public NotificationEntry getRepresentativeEntry() {
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
Mockito.doReturn(true).when(sbn).isClearable();
|
||||
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
|
||||
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
|
||||
|
||||
Mockito.doReturn(false).when(sbn).isClearable();
|
||||
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
|
||||
verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnfilteredState() {
|
||||
// GIVEN no suppressed visual effects + app not suspended
|
||||
|
||||
Reference in New Issue
Block a user