Merge "Delete NotifGroupManagerLegacy" into tm-qpr-dev am: 88b46e9016
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19735657 Change-Id: I792eb3e153e8eed082d14370e5aa92005059eb06 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -87,7 +87,6 @@ import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
|
||||
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
|
||||
@@ -311,7 +310,6 @@ public class Dependency {
|
||||
@Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
|
||||
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
|
||||
@Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
|
||||
@Inject Lazy<NotificationGroupManagerLegacy> mNotificationGroupManager;
|
||||
@Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
|
||||
@Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
|
||||
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
|
||||
@@ -524,7 +522,6 @@ public class Dependency {
|
||||
mProviders.put(NotificationLockscreenUserManager.class,
|
||||
mNotificationLockscreenUserManager::get);
|
||||
mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
|
||||
mProviders.put(NotificationGroupManagerLegacy.class, mNotificationGroupManager::get);
|
||||
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
|
||||
mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
|
||||
mProviders.put(NotificationRemoteInputManager.class,
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection.inflation;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.statusbar.NotificationUiAdjustment;
|
||||
import com.android.systemui.statusbar.notification.InflationException;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
@@ -35,22 +34,10 @@ public interface NotificationRowBinder {
|
||||
*/
|
||||
void inflateViews(
|
||||
NotificationEntry entry,
|
||||
NotifInflater.Params params,
|
||||
@NonNull NotifInflater.Params params,
|
||||
NotificationRowContentBinder.InflationCallback callback)
|
||||
throws InflationException;
|
||||
|
||||
/**
|
||||
* Called when the ranking has been updated (but not add or remove has been done). The binder
|
||||
* should inspect the old and new adjustments and re-inflate the entry's views if necessary
|
||||
* (e.g. if something important changed).
|
||||
*/
|
||||
void onNotificationRankingUpdated(
|
||||
NotificationEntry entry,
|
||||
@Nullable Integer oldImportance,
|
||||
NotificationUiAdjustment oldAdjustment,
|
||||
NotificationUiAdjustment newAdjustment,
|
||||
NotificationRowContentBinder.InflationCallback callback);
|
||||
|
||||
/**
|
||||
* Called when a notification is no longer likely to be displayed and can have its views freed.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
@@ -32,12 +33,9 @@ import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
||||
import com.android.systemui.statusbar.NotificationPresenter;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.NotificationUiAdjustment;
|
||||
import com.android.systemui.statusbar.notification.InflationException;
|
||||
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
|
||||
import com.android.systemui.statusbar.notification.NotificationClicker;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
|
||||
import com.android.systemui.statusbar.notification.icon.IconManager;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
|
||||
@@ -68,8 +66,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
private final ExpandableNotificationRowComponent.Builder
|
||||
mExpandableNotificationRowComponentBuilder;
|
||||
private final IconManager mIconManager;
|
||||
private final LowPriorityInflationHelper mLowPriorityInflationHelper;
|
||||
private final NotifPipelineFlags mNotifPipelineFlags;
|
||||
|
||||
private NotificationPresenter mPresenter;
|
||||
private NotificationListContainer mListContainer;
|
||||
@@ -86,9 +82,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
RowContentBindStage rowContentBindStage,
|
||||
Provider<RowInflaterTask> rowInflaterTaskProvider,
|
||||
ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
|
||||
IconManager iconManager,
|
||||
LowPriorityInflationHelper lowPriorityInflationHelper,
|
||||
NotifPipelineFlags notifPipelineFlags) {
|
||||
IconManager iconManager) {
|
||||
mContext = context;
|
||||
mNotifBindPipeline = notifBindPipeline;
|
||||
mRowContentBindStage = rowContentBindStage;
|
||||
@@ -98,8 +92,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
mRowInflaterTaskProvider = rowInflaterTaskProvider;
|
||||
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
|
||||
mIconManager = iconManager;
|
||||
mLowPriorityInflationHelper = lowPriorityInflationHelper;
|
||||
mNotifPipelineFlags = notifPipelineFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,13 +117,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
@Override
|
||||
public void inflateViews(
|
||||
NotificationEntry entry,
|
||||
NotifInflater.Params params,
|
||||
@NonNull NotifInflater.Params params,
|
||||
NotificationRowContentBinder.InflationCallback callback)
|
||||
throws InflationException {
|
||||
if (params == null) {
|
||||
// weak assert that the params should always be passed in the new pipeline
|
||||
mNotifPipelineFlags.checkLegacyPipelineEnabled();
|
||||
}
|
||||
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
|
||||
|
||||
if (entry.rowExists()) {
|
||||
@@ -190,39 +178,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
mBindRowCallback.onBindRow(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the views bound to an entry when the entry's ranking changes, either in-place or by
|
||||
* reinflating them.
|
||||
*
|
||||
* TODO: Should this method be in this class?
|
||||
*/
|
||||
@Override
|
||||
public void onNotificationRankingUpdated(
|
||||
NotificationEntry entry,
|
||||
@Nullable Integer oldImportance,
|
||||
NotificationUiAdjustment oldAdjustment,
|
||||
NotificationUiAdjustment newAdjustment,
|
||||
NotificationRowContentBinder.InflationCallback callback) {
|
||||
mNotifPipelineFlags.checkLegacyPipelineEnabled();
|
||||
if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
|
||||
if (entry.rowExists()) {
|
||||
ExpandableNotificationRow row = entry.getRow();
|
||||
row.reset();
|
||||
updateRow(entry, row);
|
||||
inflateContentViews(entry, null, row, callback);
|
||||
} else {
|
||||
// Once the RowInflaterTask is done, it will pick up the updated entry, so
|
||||
// no-op here.
|
||||
}
|
||||
} else {
|
||||
if (oldImportance != null && entry.getImportance() != oldImportance) {
|
||||
if (entry.rowExists()) {
|
||||
entry.getRow().onNotificationRankingUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update row after the notification has updated.
|
||||
*
|
||||
@@ -243,24 +198,12 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
|
||||
*/
|
||||
private void inflateContentViews(
|
||||
NotificationEntry entry,
|
||||
NotifInflater.Params inflaterParams,
|
||||
@NonNull NotifInflater.Params inflaterParams,
|
||||
ExpandableNotificationRow row,
|
||||
@Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
|
||||
final boolean useIncreasedCollapsedHeight =
|
||||
mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
|
||||
final boolean isLowPriority;
|
||||
if (inflaterParams != null) {
|
||||
// NEW pipeline
|
||||
isLowPriority = inflaterParams.isLowPriority();
|
||||
} else {
|
||||
// LEGACY pipeline
|
||||
mNotifPipelineFlags.checkLegacyPipelineEnabled();
|
||||
// If this is our first time inflating, we don't actually know the groupings for real
|
||||
// yet, so we might actually inflate a low priority content view incorrectly here and
|
||||
// have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
|
||||
// this should inflate the correct view.
|
||||
isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
|
||||
}
|
||||
final boolean isLowPriority = inflaterParams.isLowPriority();
|
||||
|
||||
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
|
||||
params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.legacy;
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Helper class that provide methods to help check when we need to inflate a low priority version
|
||||
* ot notification content.
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class LowPriorityInflationHelper {
|
||||
private final NotificationGroupManagerLegacy mGroupManager;
|
||||
private final NotifPipelineFlags mNotifPipelineFlags;
|
||||
|
||||
@Inject
|
||||
LowPriorityInflationHelper(
|
||||
NotificationGroupManagerLegacy groupManager,
|
||||
NotifPipelineFlags notifPipelineFlags) {
|
||||
mGroupManager = groupManager;
|
||||
mNotifPipelineFlags = notifPipelineFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the notification should inflate a low priority version of its content views.
|
||||
*/
|
||||
public boolean shouldUseLowPriorityView(NotificationEntry entry) {
|
||||
mNotifPipelineFlags.checkLegacyPipelineEnabled();
|
||||
return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
|
||||
}
|
||||
}
|
||||
@@ -1,950 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.legacy;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener;
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
||||
import com.android.systemui.util.Compile;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
* A class to handle notifications and their corresponding groups.
|
||||
* This includes:
|
||||
* 1. Determining whether an entry is a member of a group and whether it is a summary or a child
|
||||
* 2. Tracking group expansion states
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class NotificationGroupManagerLegacy implements StateListener, Dumpable {
|
||||
|
||||
private static final String TAG = "LegacyNotifGroupManager";
|
||||
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
|
||||
/**
|
||||
* The maximum amount of time (in ms) between the posting of notifications that can be
|
||||
* considered part of the same update batch.
|
||||
*/
|
||||
private static final long POST_BATCH_MAX_AGE = 5000;
|
||||
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
|
||||
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
|
||||
private final Optional<Bubbles> mBubblesOptional;
|
||||
private final GroupEventDispatcher mEventDispatcher = new GroupEventDispatcher(mGroupMap::get);
|
||||
private final HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
|
||||
private boolean mIsUpdatingUnchangedGroup;
|
||||
|
||||
@Inject
|
||||
public NotificationGroupManagerLegacy(
|
||||
StatusBarStateController statusBarStateController,
|
||||
Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier,
|
||||
Optional<Bubbles> bubblesOptional,
|
||||
DumpManager dumpManager) {
|
||||
statusBarStateController.addCallback(this);
|
||||
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
|
||||
mBubblesOptional = bubblesOptional;
|
||||
|
||||
dumpManager.registerDumpable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for changes to groups.
|
||||
*/
|
||||
public void registerGroupChangeListener(OnGroupChangeListener listener) {
|
||||
mEventDispatcher.registerGroupChangeListener(listener);
|
||||
}
|
||||
|
||||
private void setGroupExpanded(NotificationGroup group, boolean expanded) {
|
||||
group.expanded = expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* When we want to remove an entry from being tracked for grouping
|
||||
*/
|
||||
public void onEntryRemoved(NotificationEntry removed) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "onEntryRemoved: entry=" + logKey(removed));
|
||||
}
|
||||
mEventDispatcher.openBufferScope();
|
||||
onEntryRemovedInternal(removed, removed.getSbn());
|
||||
StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
|
||||
if (oldSbn != null) {
|
||||
updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
|
||||
}
|
||||
mEventDispatcher.closeBufferScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry was removed.
|
||||
*
|
||||
* @param removed the removed entry
|
||||
* @param sbn the notification the entry has, which doesn't need to be the same as it's internal
|
||||
* notification
|
||||
*/
|
||||
private void onEntryRemovedInternal(NotificationEntry removed,
|
||||
final StatusBarNotification sbn) {
|
||||
onEntryRemovedInternal(removed, sbn.getGroupKey(), sbn.isGroup(),
|
||||
sbn.getNotification().isGroupSummary());
|
||||
}
|
||||
|
||||
private void onEntryRemovedInternal(NotificationEntry removed, String notifGroupKey, boolean
|
||||
isGroup, boolean isGroupSummary) {
|
||||
String groupKey = getGroupKey(removed.getKey(), notifGroupKey);
|
||||
final NotificationGroup group = mGroupMap.get(groupKey);
|
||||
if (group == null) {
|
||||
// When an app posts 2 different notifications as summary of the same group, then a
|
||||
// cancellation of the first notification removes this group.
|
||||
// This situation is not supported and we will not allow such notifications anymore in
|
||||
// the close future. See b/23676310 for reference.
|
||||
return;
|
||||
}
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "onEntryRemovedInternal: entry=" + logKey(removed)
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
|
||||
group.children.remove(removed.getKey());
|
||||
} else {
|
||||
group.summary = null;
|
||||
}
|
||||
updateSuppression(group);
|
||||
if (group.children.isEmpty()) {
|
||||
if (group.summary == null) {
|
||||
mGroupMap.remove(groupKey);
|
||||
mEventDispatcher.notifyGroupRemoved(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntryAddedInternal(final NotificationEntry added) {
|
||||
if (added.isRowRemoved()) {
|
||||
added.setDebugThrowable(new Throwable());
|
||||
}
|
||||
final StatusBarNotification sbn = added.getSbn();
|
||||
boolean isGroupChild = isGroupChild(sbn);
|
||||
String groupKey = getGroupKey(sbn);
|
||||
NotificationGroup group = mGroupMap.get(groupKey);
|
||||
if (group == null) {
|
||||
group = new NotificationGroup(groupKey);
|
||||
mGroupMap.put(groupKey, group);
|
||||
mEventDispatcher.notifyGroupCreated(group);
|
||||
}
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "onEntryAddedInternal: entry=" + logKey(added)
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
if (isGroupChild) {
|
||||
NotificationEntry existing = group.children.get(added.getKey());
|
||||
if (existing != null && existing != added) {
|
||||
Throwable existingThrowable = existing.getDebugThrowable();
|
||||
Log.wtf(TAG, "Inconsistent entries found with the same key " + logKey(added)
|
||||
+ "existing removed: " + existing.isRowRemoved()
|
||||
+ (existingThrowable != null
|
||||
? Log.getStackTraceString(existingThrowable) + "\n" : "")
|
||||
+ " added removed" + added.isRowRemoved(), new Throwable());
|
||||
}
|
||||
group.children.put(added.getKey(), added);
|
||||
addToPostBatchHistory(group, added);
|
||||
updateSuppression(group);
|
||||
} else {
|
||||
group.summary = added;
|
||||
addToPostBatchHistory(group, added);
|
||||
group.expanded = added.areChildrenExpanded();
|
||||
updateSuppression(group);
|
||||
if (!group.children.isEmpty()) {
|
||||
ArrayList<NotificationEntry> childrenCopy =
|
||||
new ArrayList<>(group.children.values());
|
||||
for (NotificationEntry child : childrenCopy) {
|
||||
onEntryBecomingChild(child);
|
||||
}
|
||||
mEventDispatcher.notifyGroupsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
|
||||
if (didAdd) {
|
||||
trimPostBatchHistory(group.postBatchHistory);
|
||||
}
|
||||
}
|
||||
|
||||
/** remove all history that's too old to be in the batch. */
|
||||
private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
|
||||
if (postBatchHistory.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
|
||||
while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
|
||||
postBatchHistory.pollFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntryBecomingChild(NotificationEntry entry) {
|
||||
updateIsolation(entry);
|
||||
}
|
||||
|
||||
private void updateSuppression(NotificationGroup group) {
|
||||
if (group == null) {
|
||||
return;
|
||||
}
|
||||
NotificationEntry prevAlertOverride = group.alertOverride;
|
||||
group.alertOverride = getPriorityConversationAlertOverride(group);
|
||||
|
||||
int childCount = 0;
|
||||
boolean hasBubbles = false;
|
||||
for (NotificationEntry entry : group.children.values()) {
|
||||
if (mBubblesOptional.isPresent() && mBubblesOptional.get()
|
||||
.isBubbleNotificationSuppressedFromShade(
|
||||
entry.getKey(), entry.getSbn().getGroupKey())) {
|
||||
hasBubbles = true;
|
||||
} else {
|
||||
childCount++;
|
||||
}
|
||||
}
|
||||
|
||||
boolean prevSuppressed = group.suppressed;
|
||||
group.suppressed = group.summary != null && !group.expanded
|
||||
&& (childCount == 1
|
||||
|| (childCount == 0
|
||||
&& group.summary.getSbn().getNotification().isGroupSummary()
|
||||
&& (hasIsolatedChildren(group) || hasBubbles)));
|
||||
|
||||
boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
|
||||
boolean suppressionChanged = prevSuppressed != group.suppressed;
|
||||
if (alertOverrideChanged || suppressionChanged) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateSuppression:"
|
||||
+ " willNotifyListeners=" + !mIsUpdatingUnchangedGroup
|
||||
+ " changes for group:\n" + group);
|
||||
if (alertOverrideChanged) {
|
||||
Log.d(TAG, "updateSuppression: alertOverride was=" + logKey(prevAlertOverride)
|
||||
+ " now=" + logKey(group.alertOverride));
|
||||
}
|
||||
if (suppressionChanged) {
|
||||
Log.d(TAG, "updateSuppression: suppressed changed to " + group.suppressed);
|
||||
}
|
||||
}
|
||||
if (alertOverrideChanged) {
|
||||
mEventDispatcher.notifyAlertOverrideChanged(group, prevAlertOverride);
|
||||
}
|
||||
if (suppressionChanged) {
|
||||
mEventDispatcher.notifySuppressedChanged(group);
|
||||
}
|
||||
if (!mIsUpdatingUnchangedGroup) {
|
||||
mEventDispatcher.notifyGroupsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the isolated logical child of this group which is should be alerted instead.
|
||||
*
|
||||
* Notifications from priority conversations are isolated from their groups to make them more
|
||||
* prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
|
||||
* the alert. This would lead to the group alerting even though the conversation that was
|
||||
* updated was not actually a part of that group. This method finds the best priority
|
||||
* conversation in this situation, if there is one, so they can be set as the alertOverride of
|
||||
* the group.
|
||||
*
|
||||
* @param group the group to check
|
||||
* @return the entry which should receive the alert instead of the group, if any.
|
||||
*/
|
||||
@Nullable
|
||||
private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
|
||||
// GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
|
||||
// but which should be alerting (because priority conversations are isolated), find it.
|
||||
if (group == null || group.summary == null) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary"
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (isIsolated(group.summary.getKey())) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride: isolated group"
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Precondiions:
|
||||
// * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
|
||||
// * Only necessary when at least one notification in the group is on a priority channel
|
||||
if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
|
||||
== Notification.GROUP_ALERT_CHILDREN) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN"
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the important children first, copy the keys for the final importance check,
|
||||
// then add the non-isolated children to the map for unified lookup.
|
||||
HashMap<String, NotificationEntry> children = getImportantConversations(group);
|
||||
if (children == null || children.isEmpty()) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations"
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
|
||||
children.putAll(group.children);
|
||||
|
||||
// Ensure all children have GROUP_ALERT_SUMMARY
|
||||
for (NotificationEntry child : children.values()) {
|
||||
if (child.getSbn().getNotification().getGroupAlertBehavior()
|
||||
!= Notification.GROUP_ALERT_SUMMARY) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride: child != GROUP_ALERT_SUMMARY"
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a merged post history from all the children
|
||||
TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
|
||||
for (String importantChildKey : importantChildKeys) {
|
||||
NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
|
||||
combinedHistory.addAll(importantChildGroup.postBatchHistory);
|
||||
}
|
||||
trimPostBatchHistory(combinedHistory);
|
||||
|
||||
// This is a streamlined implementation of the following idea:
|
||||
// * From the subset of notifications in the latest 'batch' of updates. A batch is:
|
||||
// * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
|
||||
// * Only including notifs newer than the second-to-last post of any notification.
|
||||
// * Find the newest child in the batch -- the with the largest 'when' value.
|
||||
// * If the newest child is a priority conversation, set that as the override.
|
||||
HashSet<String> batchKeys = new HashSet<>();
|
||||
long newestChildWhen = -1;
|
||||
NotificationEntry newestChild = null;
|
||||
// Iterate backwards through the post history, tracking the child with the smallest sort key
|
||||
for (PostRecord record : combinedHistory.descendingSet()) {
|
||||
if (batchKeys.contains(record.key)) {
|
||||
// Once you see a notification again, the batch has ended
|
||||
break;
|
||||
}
|
||||
batchKeys.add(record.key);
|
||||
NotificationEntry child = children.get(record.key);
|
||||
if (child != null) {
|
||||
long childWhen = child.getSbn().getNotification().when;
|
||||
if (newestChild == null || childWhen > newestChildWhen) {
|
||||
newestChildWhen = childWhen;
|
||||
newestChild = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride:"
|
||||
+ " result=" + logKey(newestChild)
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return newestChild;
|
||||
}
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "getPriorityConversationAlertOverride:"
|
||||
+ " result=null newestChild=" + logKey(newestChild)
|
||||
+ " group=" + logGroupKey(group));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasIsolatedChildren(NotificationGroup group) {
|
||||
return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
|
||||
}
|
||||
|
||||
private int getNumberOfIsolatedChildren(String groupKey) {
|
||||
int count = 0;
|
||||
for (StatusBarNotification sbn : mIsolatedEntries.values()) {
|
||||
if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
|
||||
String groupKey = group.summary.getSbn().getGroupKey();
|
||||
HashMap<String, NotificationEntry> result = null;
|
||||
for (StatusBarNotification sbn : mIsolatedEntries.values()) {
|
||||
if (sbn.getGroupKey().equals(groupKey)) {
|
||||
NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
|
||||
if (isImportantConversation(entry)) {
|
||||
if (result == null) {
|
||||
result = new HashMap<>();
|
||||
}
|
||||
result.put(sbn.getKey(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void setStatusBarState(int newState) {
|
||||
if (newState == StatusBarState.KEYGUARD) {
|
||||
collapseGroups();
|
||||
}
|
||||
}
|
||||
|
||||
private void collapseGroups() {
|
||||
// Because notifications can become isolated when the group becomes suppressed it can
|
||||
// lead to concurrent modifications while looping. We need to make a copy.
|
||||
ArrayList<NotificationGroup> groupCopy = new ArrayList<>(mGroupMap.values());
|
||||
int size = groupCopy.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
NotificationGroup group = groupCopy.get(i);
|
||||
if (group.expanded) {
|
||||
setGroupExpanded(group, false);
|
||||
}
|
||||
updateSuppression(group);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isChildInGroup(NotificationEntry entry) {
|
||||
final StatusBarNotification sbn = entry.getSbn();
|
||||
if (!isGroupChild(sbn)) {
|
||||
return false;
|
||||
}
|
||||
NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
|
||||
if (group == null || group.summary == null || group.suppressed) {
|
||||
return false;
|
||||
}
|
||||
if (group.children.isEmpty()) {
|
||||
// If the suppression of a group changes because the last child was removed, this can
|
||||
// still be called temporarily because the child hasn't been fully removed yet. Let's
|
||||
// make sure we still return false in that case.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a {@link NotificationGroup} associated with the provided entry, this method
|
||||
* will update the suppression of that group.
|
||||
*/
|
||||
public void updateSuppression(NotificationEntry entry) {
|
||||
NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
|
||||
if (group != null) {
|
||||
updateSuppression(group);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group key. May differ from the one in the notification due to the notification
|
||||
* being temporarily isolated.
|
||||
*
|
||||
* @param sbn notification to check
|
||||
* @return the key of the notification
|
||||
*/
|
||||
private String getGroupKey(StatusBarNotification sbn) {
|
||||
return getGroupKey(sbn.getKey(), sbn.getGroupKey());
|
||||
}
|
||||
|
||||
private String getGroupKey(String key, String groupKey) {
|
||||
if (isIsolated(key)) {
|
||||
return key;
|
||||
}
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
private boolean isIsolated(String sbnKey) {
|
||||
return mIsolatedEntries.containsKey(sbnKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a notification is visually a group child.
|
||||
*
|
||||
* @param sbn notification to check
|
||||
* @return true if it is visually a group child
|
||||
*/
|
||||
private boolean isGroupChild(StatusBarNotification sbn) {
|
||||
return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary());
|
||||
}
|
||||
|
||||
private boolean isGroupChild(String key, boolean isGroup, boolean isGroupSummary) {
|
||||
if (isIsolated(key)) {
|
||||
return false;
|
||||
}
|
||||
return isGroup && !isGroupSummary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a notification that is normally part of a group should be temporarily isolated from
|
||||
* the group and put in their own group visually. This generally happens when the notification
|
||||
* is alerting.
|
||||
*
|
||||
* @param entry the notification to check
|
||||
* @return true if the entry should be isolated
|
||||
*/
|
||||
private boolean shouldIsolate(NotificationEntry entry) {
|
||||
StatusBarNotification sbn = entry.getSbn();
|
||||
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
|
||||
return false;
|
||||
}
|
||||
if (isImportantConversation(entry)) {
|
||||
return true;
|
||||
}
|
||||
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
|
||||
return (sbn.getNotification().fullScreenIntent != null
|
||||
|| notificationGroup == null
|
||||
|| !notificationGroup.expanded
|
||||
|| isGroupNotFullyVisible(notificationGroup));
|
||||
}
|
||||
|
||||
private boolean isImportantConversation(NotificationEntry entry) {
|
||||
int peopleNotificationType =
|
||||
mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
|
||||
return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolate a notification from its group so that it visually shows as its own group.
|
||||
*
|
||||
* @param entry the notification to isolate
|
||||
*/
|
||||
private void isolateNotification(NotificationEntry entry) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "isolateNotification: entry=" + logKey(entry));
|
||||
}
|
||||
// We will be isolated now, so lets update the groups
|
||||
onEntryRemovedInternal(entry, entry.getSbn());
|
||||
|
||||
mIsolatedEntries.put(entry.getKey(), entry.getSbn());
|
||||
|
||||
onEntryAddedInternal(entry);
|
||||
// We also need to update the suppression of the old group, because this call comes
|
||||
// even before the groupManager knows about the notification at all.
|
||||
// When the notification gets added afterwards it is already isolated and therefore
|
||||
// it doesn't lead to an update.
|
||||
updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
|
||||
mEventDispatcher.notifyGroupsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the isolation of an entry, splitting it from the group.
|
||||
*/
|
||||
private void updateIsolation(NotificationEntry entry) {
|
||||
// We need to buffer a few events because we do isolation changes in 3 steps:
|
||||
// removeInternal, update mIsolatedEntries, addInternal. This means that often the
|
||||
// alertOverride will update on the removal, however processing the event in that case can
|
||||
// cause problems because the mIsolatedEntries map is not in its final state, so the event
|
||||
// listener may be unable to correctly determine the true state of the group. By delaying
|
||||
// the alertOverride change until after the add phase, we can ensure that listeners only
|
||||
// have to handle a consistent state.
|
||||
mEventDispatcher.openBufferScope();
|
||||
boolean isIsolated = isIsolated(entry.getSbn().getKey());
|
||||
if (shouldIsolate(entry)) {
|
||||
if (!isIsolated) {
|
||||
isolateNotification(entry);
|
||||
}
|
||||
} else if (isIsolated) {
|
||||
stopIsolatingNotification(entry);
|
||||
}
|
||||
mEventDispatcher.closeBufferScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop isolating a notification and re-group it with its original logical group.
|
||||
*
|
||||
* @param entry the notification to un-isolate
|
||||
*/
|
||||
private void stopIsolatingNotification(NotificationEntry entry) {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "stopIsolatingNotification: entry=" + logKey(entry));
|
||||
}
|
||||
// not isolated anymore, we need to update the groups
|
||||
onEntryRemovedInternal(entry, entry.getSbn());
|
||||
mIsolatedEntries.remove(entry.getKey());
|
||||
onEntryAddedInternal(entry);
|
||||
mEventDispatcher.notifyGroupsChanged();
|
||||
}
|
||||
|
||||
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
|
||||
return notificationGroup.summary == null
|
||||
|| notificationGroup.summary.isGroupNotFullyVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String[] args) {
|
||||
pw.println("GroupManagerLegacy state:");
|
||||
pw.println(" number of groups: " + mGroupMap.size());
|
||||
for (Map.Entry<String, NotificationGroup> entry : mGroupMap.entrySet()) {
|
||||
pw.println("\n key: " + logKey(entry.getKey())); pw.println(entry.getValue());
|
||||
}
|
||||
pw.println("\n isolated entries: " + mIsolatedEntries.size());
|
||||
for (Map.Entry<String, StatusBarNotification> entry : mIsolatedEntries.entrySet()) {
|
||||
pw.print(" "); pw.print(logKey(entry.getKey()));
|
||||
pw.print(", "); pw.println(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(int newState) {
|
||||
setStatusBarState(newState);
|
||||
}
|
||||
|
||||
/** Get the group key, reformatted for logging, for the (optional) group */
|
||||
private static String logGroupKey(NotificationGroup group) {
|
||||
if (group == null) {
|
||||
return "null";
|
||||
}
|
||||
return logKey(group.groupKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* A record of a notification being posted, containing the time of the post and the key of the
|
||||
* notification entry. These are stored in a TreeSet by the NotificationGroup and used to
|
||||
* calculate a batch of notifications.
|
||||
*/
|
||||
public static class PostRecord implements Comparable<PostRecord> {
|
||||
public final long postTime;
|
||||
public final String key;
|
||||
|
||||
/** constructs a record containing the post time and key from the notification entry */
|
||||
public PostRecord(@NonNull NotificationEntry entry) {
|
||||
this.postTime = entry.getSbn().getPostTime();
|
||||
this.key = entry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PostRecord o) {
|
||||
int postTimeComparison = Long.compare(this.postTime, o.postTime);
|
||||
return postTimeComparison == 0
|
||||
? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
|
||||
: postTimeComparison;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PostRecord that = (PostRecord) o;
|
||||
return postTime == that.postTime && key.equals(that.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(postTime, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a notification group in the notification shade.
|
||||
*/
|
||||
public static class NotificationGroup {
|
||||
public final String groupKey;
|
||||
public final HashMap<String, NotificationEntry> children = new HashMap<>();
|
||||
public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
|
||||
public NotificationEntry summary;
|
||||
public boolean expanded;
|
||||
/**
|
||||
* Is this notification group suppressed, i.e its summary is hidden
|
||||
*/
|
||||
public boolean suppressed;
|
||||
/**
|
||||
* The child (which is isolated from this group) to which the alert should be transferred,
|
||||
* due to priority conversations.
|
||||
*/
|
||||
public NotificationEntry alertOverride;
|
||||
|
||||
NotificationGroup(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(" groupKey: ").append(groupKey);
|
||||
sb.append("\n summary:");
|
||||
appendEntry(sb, summary);
|
||||
sb.append("\n children size: ").append(children.size());
|
||||
for (NotificationEntry child : children.values()) {
|
||||
appendEntry(sb, child);
|
||||
}
|
||||
sb.append("\n alertOverride:");
|
||||
appendEntry(sb, alertOverride);
|
||||
sb.append("\n summary suppressed: ").append(suppressed);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void appendEntry(StringBuilder sb, NotificationEntry entry) {
|
||||
sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
|
||||
if (entry != null && entry.getDebugThrowable() != null) {
|
||||
sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
|
||||
* When buffering, instead of notifying the listeners it will set internal state that will allow
|
||||
* it to notify listeners of those events later
|
||||
*/
|
||||
static class GroupEventDispatcher {
|
||||
private final Function<String, NotificationGroup> mGroupMapGetter;
|
||||
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
|
||||
private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
|
||||
private final HashMap<String, Boolean> mOldSuppressedByGroup = new HashMap<>();
|
||||
private int mBufferScopeDepth = 0;
|
||||
private boolean mDidGroupsChange = false;
|
||||
|
||||
GroupEventDispatcher(Function<String, NotificationGroup> groupMapGetter) {
|
||||
mGroupMapGetter = requireNonNull(groupMapGetter);
|
||||
}
|
||||
|
||||
void registerGroupChangeListener(OnGroupChangeListener listener) {
|
||||
mGroupChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
private boolean isBuffering() {
|
||||
return mBufferScopeDepth > 0;
|
||||
}
|
||||
|
||||
void notifyAlertOverrideChanged(NotificationGroup group,
|
||||
NotificationEntry oldAlertOverride) {
|
||||
if (isBuffering()) {
|
||||
// The value in this map is the override before the event. If there is an entry
|
||||
// already in the map, then we are effectively coalescing two events, which means
|
||||
// we need to preserve the original initial value.
|
||||
if (!mOldAlertOverrideByGroup.containsKey(group.groupKey)) {
|
||||
mOldAlertOverrideByGroup.put(group.groupKey, oldAlertOverride);
|
||||
}
|
||||
} else {
|
||||
for (OnGroupChangeListener listener : mGroupChangeListeners) {
|
||||
listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
|
||||
group.alertOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notifySuppressedChanged(NotificationGroup group) {
|
||||
if (isBuffering()) {
|
||||
// The value in this map is the 'suppressed' before the event. If there is a value
|
||||
// already in the map, then we are effectively coalescing two events, which means
|
||||
// we need to preserve the original initial value.
|
||||
mOldSuppressedByGroup.putIfAbsent(group.groupKey, !group.suppressed);
|
||||
} else {
|
||||
for (OnGroupChangeListener listener : mGroupChangeListeners) {
|
||||
listener.onGroupSuppressionChanged(group, group.suppressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notifyGroupsChanged() {
|
||||
if (isBuffering()) {
|
||||
mDidGroupsChange = true;
|
||||
} else {
|
||||
for (OnGroupChangeListener listener : mGroupChangeListeners) {
|
||||
listener.onGroupsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void notifyGroupCreated(NotificationGroup group) {
|
||||
// NOTE: given how this event is used, it doesn't need to be buffered right now
|
||||
final String groupKey = group.groupKey;
|
||||
for (OnGroupChangeListener listener : mGroupChangeListeners) {
|
||||
listener.onGroupCreated(group, groupKey);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyGroupRemoved(NotificationGroup group) {
|
||||
// NOTE: given how this event is used, it doesn't need to be buffered right now
|
||||
final String groupKey = group.groupKey;
|
||||
for (OnGroupChangeListener listener : mGroupChangeListeners) {
|
||||
listener.onGroupRemoved(group, groupKey);
|
||||
}
|
||||
}
|
||||
|
||||
void openBufferScope() {
|
||||
mBufferScopeDepth++;
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "openBufferScope: scopeDepth=" + mBufferScopeDepth);
|
||||
}
|
||||
}
|
||||
|
||||
void closeBufferScope() {
|
||||
mBufferScopeDepth--;
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "closeBufferScope: scopeDepth=" + mBufferScopeDepth);
|
||||
}
|
||||
// Flush buffered events if the last buffer scope has closed
|
||||
if (!isBuffering()) {
|
||||
flushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBuffer() {
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "flushBuffer: "
|
||||
+ " suppressed.size=" + mOldSuppressedByGroup.size()
|
||||
+ " alertOverride.size=" + mOldAlertOverrideByGroup.size()
|
||||
+ " mDidGroupsChange=" + mDidGroupsChange);
|
||||
}
|
||||
// alert all group suppressed changes for groups that were not removed
|
||||
for (Map.Entry<String, Boolean> entry : mOldSuppressedByGroup.entrySet()) {
|
||||
NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
|
||||
if (group == null) {
|
||||
// The group can be null if this suppressed changed before the group was
|
||||
// permanently removed, meaning that there's no guarantee that listeners will
|
||||
// that field clear.
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "flushBuffer: suppressed:"
|
||||
+ " cannot report for removed group: " + logKey(entry.getKey()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
boolean oldSuppressed = entry.getValue();
|
||||
if (group.suppressed == oldSuppressed) {
|
||||
// If the final suppressed equals the initial, it means we coalesced two
|
||||
// events which undid the change, so we can drop it entirely.
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "flushBuffer: suppressed:"
|
||||
+ " did not change for group: " + logKey(entry.getKey()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
notifySuppressedChanged(group);
|
||||
}
|
||||
mOldSuppressedByGroup.clear();
|
||||
// alert all group alert override changes for groups that were not removed
|
||||
for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
|
||||
NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
|
||||
if (group == null) {
|
||||
// The group can be null if this alertOverride changed before the group was
|
||||
// permanently removed, meaning that there's no guarantee that listeners will
|
||||
// that field clear.
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "flushBuffer: alertOverride:"
|
||||
+ " cannot report for removed group: " + entry.getKey());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
NotificationEntry oldAlertOverride = entry.getValue();
|
||||
if (group.alertOverride == oldAlertOverride) {
|
||||
// If the final alertOverride equals the initial, it means we coalesced two
|
||||
// events which undid the change, so we can drop it entirely.
|
||||
if (SPEW) {
|
||||
Log.d(TAG, "flushBuffer: alertOverride:"
|
||||
+ " did not change for group: " + logKey(entry.getKey()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
notifyAlertOverrideChanged(group, oldAlertOverride);
|
||||
}
|
||||
mOldAlertOverrideByGroup.clear();
|
||||
// alert that groups changed
|
||||
if (mDidGroupsChange) {
|
||||
notifyGroupsChanged();
|
||||
mDidGroupsChange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for group changes not including group expansion changes which are handled by
|
||||
* {@link OnGroupExpansionChangeListener}.
|
||||
*/
|
||||
public interface OnGroupChangeListener {
|
||||
/**
|
||||
* A new group has been created.
|
||||
*
|
||||
* @param group the group that was created
|
||||
* @param groupKey the group's key
|
||||
*/
|
||||
default void onGroupCreated(
|
||||
NotificationGroup group,
|
||||
String groupKey) {}
|
||||
|
||||
/**
|
||||
* A group has been removed.
|
||||
*
|
||||
* @param group the group that was removed
|
||||
* @param groupKey the group's key
|
||||
*/
|
||||
default void onGroupRemoved(
|
||||
NotificationGroup group,
|
||||
String groupKey) {}
|
||||
|
||||
/**
|
||||
* The suppression of a group has changed.
|
||||
*
|
||||
* @param group the group that has changed
|
||||
* @param suppressed true if the group is now suppressed, false o/w
|
||||
*/
|
||||
default void onGroupSuppressionChanged(
|
||||
NotificationGroup group,
|
||||
boolean suppressed) {}
|
||||
|
||||
/**
|
||||
* The alert override of a group has changed.
|
||||
*
|
||||
* @param group the group that has changed
|
||||
* @param oldAlertOverride the previous notification to which the group's alerts were sent
|
||||
* @param newAlertOverride the notification to which the group's alerts should now be sent
|
||||
*/
|
||||
default void onGroupAlertOverrideChanged(
|
||||
NotificationGroup group,
|
||||
@Nullable NotificationEntry oldAlertOverride,
|
||||
@Nullable NotificationEntry newAlertOverride) {}
|
||||
|
||||
/**
|
||||
* The groups have changed. This can happen if the isolation of a child has changes or if a
|
||||
* group became suppressed / unsuppressed
|
||||
*/
|
||||
default void onGroupsChanged() {}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import android.os.Handler;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
@@ -37,9 +36,7 @@ import com.android.systemui.settings.UserContextProvider;
|
||||
import com.android.systemui.shade.NotifPanelEventsModule;
|
||||
import com.android.systemui.shade.ShadeController;
|
||||
import com.android.systemui.statusbar.NotificationListener;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
|
||||
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
|
||||
@@ -53,7 +50,6 @@ import com.android.systemui.statusbar.notification.collection.inflation.BindEven
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
|
||||
@@ -84,7 +80,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan
|
||||
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||
import com.android.systemui.util.leak.LeakDetector;
|
||||
import com.android.systemui.wmshell.BubblesManager;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -118,16 +113,8 @@ public interface NotificationsModule {
|
||||
@SysUISingleton
|
||||
@Provides
|
||||
static NotificationEntryManager provideNotificationEntryManager(
|
||||
NotificationEntryManagerLogger logger,
|
||||
NotificationGroupManagerLegacy groupManager,
|
||||
NotifPipelineFlags notifPipelineFlags,
|
||||
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
|
||||
LeakDetector leakDetector,
|
||||
IStatusBarService statusBarService,
|
||||
@Background Executor bgExecutor) {
|
||||
return new NotificationEntryManager(
|
||||
logger
|
||||
);
|
||||
NotificationEntryManagerLogger logger) {
|
||||
return new NotificationEntryManager(logger);
|
||||
}
|
||||
|
||||
/** Provides an instance of {@link NotificationGutsManager} */
|
||||
@@ -141,7 +128,6 @@ public interface NotificationsModule {
|
||||
AccessibilityManager accessibilityManager,
|
||||
HighPriorityProvider highPriorityProvider,
|
||||
INotificationManager notificationManager,
|
||||
NotificationEntryManager notificationEntryManager,
|
||||
PeopleSpaceWidgetManager peopleSpaceWidgetManager,
|
||||
LauncherApps launcherApps,
|
||||
ShortcutManager shortcutManager,
|
||||
|
||||
@@ -87,8 +87,6 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
|
||||
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
||||
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
|
||||
@@ -633,7 +631,6 @@ public class NotificationStackScrollLayoutController {
|
||||
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
|
||||
CentralSurfaces centralSurfaces,
|
||||
ScrimController scrimController,
|
||||
NotificationGroupManagerLegacy legacyGroupManager,
|
||||
GroupExpansionManager groupManager,
|
||||
@SilentHeader SectionHeaderController silentHeaderController,
|
||||
NotifPipeline notifPipeline,
|
||||
@@ -677,12 +674,6 @@ public class NotificationStackScrollLayoutController {
|
||||
mJankMonitor = jankMonitor;
|
||||
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
|
||||
mGroupExpansionManager = groupManager;
|
||||
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
|
||||
@Override
|
||||
public void onGroupsChanged() {
|
||||
mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
|
||||
}
|
||||
});
|
||||
mSilentHeaderController = silentHeaderController;
|
||||
mNotifPipeline = notifPipeline;
|
||||
mNotifCollection = notifCollection;
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.legacy
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.GroupEventDispatcher
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper
|
||||
class GroupEventDispatcherTest : SysuiTestCase() {
|
||||
val groupMap = mutableMapOf<String, NotificationGroup>()
|
||||
val groupTestHelper = NotificationGroupTestHelper(mContext)
|
||||
|
||||
private val dispatcher = GroupEventDispatcher(groupMap::get)
|
||||
private val listener: OnGroupChangeListener = mock()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
dispatcher.registerGroupChangeListener(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupsChangedUnbuffered() {
|
||||
dispatcher.notifyGroupsChanged()
|
||||
verify(listener).onGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupsChangedBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
dispatcher.notifyGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupsChangedDoubleBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
dispatcher.notifyGroupsChanged()
|
||||
dispatcher.openBufferScope() // open a nested buffer scope
|
||||
dispatcher.notifyGroupsChanged()
|
||||
dispatcher.closeBufferScope() // should NOT flush events
|
||||
dispatcher.notifyGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope() // this SHOULD flush events
|
||||
verify(listener).onGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupsChangedBufferCoalesces() {
|
||||
dispatcher.openBufferScope()
|
||||
dispatcher.notifyGroupsChanged()
|
||||
dispatcher.notifyGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupsChanged()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupCreatedIsNeverBuffered() {
|
||||
val group = addGroup(1)
|
||||
|
||||
dispatcher.openBufferScope()
|
||||
dispatcher.notifyGroupCreated(group)
|
||||
verify(listener).onGroupCreated(group, group.groupKey)
|
||||
verifyNoMoreInteractions(listener)
|
||||
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnGroupRemovedIsNeverBuffered() {
|
||||
val group = addGroup(1)
|
||||
|
||||
dispatcher.openBufferScope()
|
||||
dispatcher.notifyGroupRemoved(group)
|
||||
verify(listener).onGroupRemoved(group, group.groupKey)
|
||||
verifyNoMoreInteractions(listener)
|
||||
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideAddedUnbuffered() {
|
||||
val group = addGroup(1)
|
||||
val newAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = newAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, null)
|
||||
verify(listener).onGroupAlertOverrideChanged(group, null, newAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideRemovedUnbuffered() {
|
||||
val group = addGroup(1)
|
||||
val oldAlertEntry = groupTestHelper.createChildNotification()
|
||||
dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
|
||||
verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, null)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideChangedUnbuffered() {
|
||||
val group = addGroup(1)
|
||||
val oldAlertEntry = groupTestHelper.createChildNotification()
|
||||
val newAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = newAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
|
||||
verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideChangedBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
val oldAlertEntry = groupTestHelper.createChildNotification()
|
||||
val newAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = newAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideIgnoredIfRemoved() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
val oldAlertEntry = groupTestHelper.createChildNotification()
|
||||
val newAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = newAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
groupMap.clear()
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideMultipleChangesBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
val oldAlertEntry = groupTestHelper.createChildNotification()
|
||||
val newAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = null
|
||||
dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
|
||||
group.alertOverride = newAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, null)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideTemporaryValueSwallowed() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
val stableAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = null
|
||||
dispatcher.notifyAlertOverrideChanged(group, stableAlertEntry)
|
||||
group.alertOverride = stableAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, null)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAlertOverrideTemporaryNullSwallowed() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
val temporaryAlertEntry = groupTestHelper.createChildNotification()
|
||||
group.alertOverride = temporaryAlertEntry
|
||||
dispatcher.notifyAlertOverrideChanged(group, null)
|
||||
group.alertOverride = null
|
||||
dispatcher.notifyAlertOverrideChanged(group, temporaryAlertEntry)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOnUnbuffered() {
|
||||
val group = addGroup(1)
|
||||
group.suppressed = true
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verify(listener).onGroupSuppressionChanged(group, true)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOffUnbuffered() {
|
||||
val group = addGroup(1)
|
||||
group.suppressed = false
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verify(listener).onGroupSuppressionChanged(group, false)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOnBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
group.suppressed = false
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupSuppressionChanged(group, false)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOnIgnoredIfRemoved() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
group.suppressed = false
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verifyNoMoreInteractions(listener)
|
||||
groupMap.clear()
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOnOffBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
group.suppressed = true
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
group.suppressed = false
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuppressOnOffOnBuffered() {
|
||||
dispatcher.openBufferScope()
|
||||
val group = addGroup(1)
|
||||
group.suppressed = true
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
group.suppressed = false
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
group.suppressed = true
|
||||
dispatcher.notifySuppressedChanged(group)
|
||||
verifyNoMoreInteractions(listener)
|
||||
dispatcher.closeBufferScope()
|
||||
verify(listener).onGroupSuppressionChanged(group, true)
|
||||
verifyNoMoreInteractions(listener)
|
||||
}
|
||||
|
||||
private fun addGroup(id: Int): NotificationGroup {
|
||||
val group = NotificationGroup("group:$id")
|
||||
groupMap[group.groupKey] = group
|
||||
return group
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.res.Resources;
|
||||
import android.metrics.LogMaker;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -42,11 +41,9 @@ import com.android.internal.jank.InteractionJankMonitor;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||
import com.android.systemui.classifier.FalsingManagerFake;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.media.KeyguardMediaController;
|
||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||
@@ -61,12 +58,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.RemoteInputController;
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifCollection;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
|
||||
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
|
||||
@@ -112,7 +106,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
@Mock private KeyguardMediaController mKeyguardMediaController;
|
||||
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
|
||||
@Mock private KeyguardBypassController mKeyguardBypassController;
|
||||
@Mock private SysuiColorExtractor mColorExtractor;
|
||||
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
|
||||
@Mock private MetricsLogger mMetricsLogger;
|
||||
@Mock private DumpManager mDumpManager;
|
||||
@@ -122,19 +115,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
|
||||
@Mock private CentralSurfaces mCentralSurfaces;
|
||||
@Mock private ScrimController mScrimController;
|
||||
@Mock private NotificationGroupManagerLegacy mGroupManagerLegacy;
|
||||
@Mock private GroupExpansionManager mGroupExpansionManager;
|
||||
@Mock private SectionHeaderController mSilentHeaderController;
|
||||
@Mock private NotifPipelineFlags mNotifPipelineFlags;
|
||||
@Mock private NotifPipeline mNotifPipeline;
|
||||
@Mock private NotifCollection mNotifCollection;
|
||||
@Mock private NotificationEntryManager mEntryManager;
|
||||
@Mock private IStatusBarService mIStatusBarService;
|
||||
@Mock private UiEventLogger mUiEventLogger;
|
||||
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
|
||||
@Mock private LayoutInflater mLayoutInflater;
|
||||
@Mock private NotificationRemoteInputManager mRemoteInputManager;
|
||||
@Mock private VisualStabilityManager mVisualStabilityManager;
|
||||
@Mock private ShadeController mShadeController;
|
||||
@Mock private InteractionJankMonitor mJankMonitor;
|
||||
@Mock private StackStateLogger mStackLogger;
|
||||
@@ -176,7 +164,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
|
||||
mNotificationSwipeHelperBuilder,
|
||||
mCentralSurfaces,
|
||||
mScrimController,
|
||||
mGroupManagerLegacy,
|
||||
mGroupExpansionManager,
|
||||
mSilentHeaderController,
|
||||
mNotifPipeline,
|
||||
|
||||
Reference in New Issue
Block a user