Merge changes from topic "dedupe-lockscreen" into sc-dev
* changes: Dedupe smartspace and notification content on lockscreen Break circular dependencies in notif code
This commit is contained in:
@@ -49,4 +49,6 @@
|
||||
<bool name="flag_ongoing_call_status_bar_chip">true</bool>
|
||||
|
||||
<bool name="flag_smartspace">false</bool>
|
||||
|
||||
<bool name="flag_smartspace_deduping">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -92,4 +92,8 @@ public class FeatureFlags {
|
||||
public boolean isSmartspaceEnabled() {
|
||||
return mFlagReader.isEnabled(R.bool.flag_smartspace);
|
||||
}
|
||||
|
||||
public boolean isSmartspaceDedupingEnabled() {
|
||||
return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ public interface NotificationLockscreenUserManager {
|
||||
/** Adds a listener to be notified when the current user changes. */
|
||||
void addUserChangedListener(UserChangedListener listener);
|
||||
|
||||
/**
|
||||
* Registers a [KeyguardNotificationSuppressor] that will be consulted during
|
||||
* {@link #shouldShowOnKeyguard(NotificationEntry)}
|
||||
*/
|
||||
void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor);
|
||||
|
||||
/**
|
||||
* Removes a listener previously registered with
|
||||
* {@link #addUserChangedListener(UserChangedListener)}
|
||||
@@ -88,4 +94,9 @@ public interface NotificationLockscreenUserManager {
|
||||
default void onUserChanged(int userId) {}
|
||||
default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
|
||||
}
|
||||
|
||||
/** Used to hide notifications on the lockscreen */
|
||||
interface KeyguardNotificationSuppressor {
|
||||
boolean shouldSuppressOnKeyguard(NotificationEntry entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ public class NotificationLockscreenUserManagerImpl implements
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
protected KeyguardManager mKeyguardManager;
|
||||
private int mState = StatusBarState.SHADE;
|
||||
private List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
|
||||
|
||||
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@@ -343,6 +344,11 @@ public class NotificationLockscreenUserManagerImpl implements
|
||||
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < mKeyguardSuppressors.size(); i++) {
|
||||
if (mKeyguardSuppressors.get(i).shouldSuppressOnKeyguard(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
boolean exceedsPriorityThreshold;
|
||||
if (hideSilentNotificationsOnLockscreen()) {
|
||||
exceedsPriorityThreshold =
|
||||
@@ -620,6 +626,11 @@ public class NotificationLockscreenUserManagerImpl implements
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor) {
|
||||
mKeyguardSuppressors.add(suppressor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserChangedListener(UserChangedListener listener) {
|
||||
mListeners.remove(listener);
|
||||
|
||||
@@ -46,8 +46,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
|
||||
import com.android.systemui.statusbar.NotificationUiAdjustment;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRankerStub;
|
||||
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;
|
||||
@@ -138,12 +139,11 @@ public class NotificationEntryManager implements
|
||||
private final LeakDetector mLeakDetector;
|
||||
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
|
||||
|
||||
private final KeyguardEnvironment mKeyguardEnvironment;
|
||||
private final NotificationGroupManagerLegacy mGroupManager;
|
||||
private final Lazy<NotificationRankingManager> mRankingManager;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
|
||||
|
||||
private LegacyNotificationRanker mRanker = new LegacyNotificationRankerStub();
|
||||
private NotificationPresenter mPresenter;
|
||||
private RankingMap mLatestRankingMap;
|
||||
|
||||
@@ -200,8 +200,6 @@ public class NotificationEntryManager implements
|
||||
public NotificationEntryManager(
|
||||
NotificationEntryManagerLogger logger,
|
||||
NotificationGroupManagerLegacy groupManager,
|
||||
Lazy<NotificationRankingManager> rankingManager,
|
||||
KeyguardEnvironment keyguardEnvironment,
|
||||
FeatureFlags featureFlags,
|
||||
Lazy<NotificationRowBinder> notificationRowBinderLazy,
|
||||
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
|
||||
@@ -211,8 +209,6 @@ public class NotificationEntryManager implements
|
||||
) {
|
||||
mLogger = logger;
|
||||
mGroupManager = groupManager;
|
||||
mRankingManager = rankingManager;
|
||||
mKeyguardEnvironment = keyguardEnvironment;
|
||||
mFeatureFlags = featureFlags;
|
||||
mNotificationRowBinderLazy = notificationRowBinderLazy;
|
||||
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
|
||||
@@ -226,6 +222,10 @@ public class NotificationEntryManager implements
|
||||
notificationListener.addNotificationHandler(mNotifListener);
|
||||
}
|
||||
|
||||
public void setRanker(LegacyNotificationRanker ranker) {
|
||||
mRanker = ranker;
|
||||
}
|
||||
|
||||
/** Adds a {@link NotificationEntryListener}. */
|
||||
public void addNotificationEntryListener(NotificationEntryListener listener) {
|
||||
mNotificationEntryListeners.add(listener);
|
||||
@@ -419,7 +419,7 @@ public class NotificationEntryManager implements
|
||||
|
||||
mActiveNotifications.put(entry.getKey(), entry);
|
||||
mGroupManager.onEntryAdded(entry);
|
||||
updateRankingAndSort(mRankingManager.get().getRankingMap(), "addEntryInternalInternal");
|
||||
updateRankingAndSort(mRanker.getRankingMap(), "addEntryInternalInternal");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -698,13 +698,6 @@ public class NotificationEntryManager implements
|
||||
|
||||
updateNotifications("updateNotificationInternal");
|
||||
|
||||
if (DEBUG) {
|
||||
// Is this for you?
|
||||
boolean isForCurrentUser = mKeyguardEnvironment
|
||||
.isNotificationForCurrentProfiles(notification);
|
||||
Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
|
||||
}
|
||||
|
||||
for (NotificationEntryListener listener : mNotificationEntryListeners) {
|
||||
listener.onPostEntryUpdated(entry);
|
||||
}
|
||||
@@ -862,8 +855,7 @@ public class NotificationEntryManager implements
|
||||
final int len = mActiveNotifications.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
NotificationEntry entry = mActiveNotifications.valueAt(i);
|
||||
final StatusBarNotification sbn = entry.getSbn();
|
||||
if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
|
||||
if (!mRanker.isNotificationForCurrentProfiles(entry)) {
|
||||
continue;
|
||||
}
|
||||
filtered.add(entry);
|
||||
@@ -886,13 +878,13 @@ public class NotificationEntryManager implements
|
||||
|
||||
/** Resorts / filters the current notification set with the current RankingMap */
|
||||
public void reapplyFilterAndSort(String reason) {
|
||||
updateRankingAndSort(mRankingManager.get().getRankingMap(), reason);
|
||||
updateRankingAndSort(mRanker.getRankingMap(), reason);
|
||||
}
|
||||
|
||||
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
|
||||
private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
|
||||
mSortedAndFiltered.clear();
|
||||
mSortedAndFiltered.addAll(mRankingManager.get().updateRanking(
|
||||
mSortedAndFiltered.addAll(mRanker.updateRanking(
|
||||
rankingMap, mActiveNotifications.values(), reason));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,13 @@ import android.os.RemoteException;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.ForegroundServiceController;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.media.MediaFeatureFlag;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.phone.ShadeController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -46,68 +45,43 @@ import javax.inject.Inject;
|
||||
public class NotificationFilter {
|
||||
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final KeyguardEnvironment mKeyguardEnvironment;
|
||||
private final ForegroundServiceController mForegroundServiceController;
|
||||
private final NotificationLockscreenUserManager mUserManager;
|
||||
private final Boolean mIsMediaFlagEnabled;
|
||||
|
||||
private NotificationEntryManager.KeyguardEnvironment mEnvironment;
|
||||
private ShadeController mShadeController;
|
||||
private ForegroundServiceController mFsc;
|
||||
private NotificationLockscreenUserManager mUserManager;
|
||||
|
||||
@Inject
|
||||
public NotificationFilter(
|
||||
StatusBarStateController statusBarStateController,
|
||||
KeyguardEnvironment keyguardEnvironment,
|
||||
ForegroundServiceController foregroundServiceController,
|
||||
NotificationLockscreenUserManager userManager,
|
||||
MediaFeatureFlag mediaFeatureFlag) {
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mKeyguardEnvironment = keyguardEnvironment;
|
||||
mForegroundServiceController = foregroundServiceController;
|
||||
mUserManager = userManager;
|
||||
mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled();
|
||||
}
|
||||
|
||||
private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
|
||||
if (mEnvironment == null) {
|
||||
mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class);
|
||||
}
|
||||
return mEnvironment;
|
||||
}
|
||||
|
||||
private ShadeController getShadeController() {
|
||||
if (mShadeController == null) {
|
||||
mShadeController = Dependency.get(ShadeController.class);
|
||||
}
|
||||
return mShadeController;
|
||||
}
|
||||
|
||||
private ForegroundServiceController getFsc() {
|
||||
if (mFsc == null) {
|
||||
mFsc = Dependency.get(ForegroundServiceController.class);
|
||||
}
|
||||
return mFsc;
|
||||
}
|
||||
|
||||
private NotificationLockscreenUserManager getUserManager() {
|
||||
if (mUserManager == null) {
|
||||
mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
|
||||
}
|
||||
return mUserManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the provided notification should NOT be shown right now.
|
||||
*/
|
||||
public boolean shouldFilterOut(NotificationEntry entry) {
|
||||
final StatusBarNotification sbn = entry.getSbn();
|
||||
if (!(getEnvironment().isDeviceProvisioned()
|
||||
if (!(mKeyguardEnvironment.isDeviceProvisioned()
|
||||
|| showNotificationEvenIfUnprovisioned(sbn))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
|
||||
if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
|
||||
if (mUserManager.isLockscreenPublicMode(sbn.getUserId())
|
||||
&& (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
|
||||
|| getUserManager().shouldHideNotifications(sbn.getUserId())
|
||||
|| getUserManager().shouldHideNotifications(sbn.getKey()))) {
|
||||
|| mUserManager.shouldHideNotifications(sbn.getUserId())
|
||||
|| mUserManager.shouldHideNotifications(sbn.getKey()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -123,8 +97,8 @@ public class NotificationFilter {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getFsc().isDisclosureNotification(sbn)
|
||||
&& !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
|
||||
if (mForegroundServiceController.isDisclosureNotification(sbn)
|
||||
&& !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
|
||||
// this is a foreground-service disclosure for a user that does not need to show one
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,6 +185,11 @@ public class NotifCollection implements Dumpable {
|
||||
mBuildListener = buildListener;
|
||||
}
|
||||
|
||||
/** @see NotifPipeline#getEntry(String) () */
|
||||
NotificationEntry getEntry(String key) {
|
||||
return mNotificationSet.get(key);
|
||||
}
|
||||
|
||||
/** @see NotifPipeline#getAllNotifs() */
|
||||
Collection<NotificationEntry> getAllNotifs() {
|
||||
Assert.isMainThread();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
|
||||
@@ -89,6 +91,7 @@ public class NotifPipeline implements CommonNotifCollection {
|
||||
*
|
||||
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
|
||||
*/
|
||||
@Override
|
||||
public Collection<NotificationEntry> getAllNotifs() {
|
||||
return mNotifCollection.getAllNotifs();
|
||||
}
|
||||
@@ -98,6 +101,14 @@ public class NotifPipeline implements CommonNotifCollection {
|
||||
mNotifCollection.addCollectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the NotificationEntry associated with [key].
|
||||
*/
|
||||
@Nullable
|
||||
public NotificationEntry getEntry(String key) {
|
||||
return mNotifCollection.getEntry(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a lifetime extender. Lifetime extenders can cause notifications that have been
|
||||
* dismissed or retracted by system server to be temporarily retained in the collection.
|
||||
|
||||
@@ -23,9 +23,11 @@ import android.service.notification.NotificationListenerService.Ranking
|
||||
import android.service.notification.NotificationListenerService.RankingMap
|
||||
import android.service.notification.StatusBarNotification
|
||||
import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
|
||||
import com.android.systemui.statusbar.notification.NotificationFilter
|
||||
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
|
||||
@@ -39,7 +41,6 @@ import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import dagger.Lazy
|
||||
import java.util.Objects
|
||||
import javax.inject.Inject
|
||||
import kotlin.Comparator
|
||||
|
||||
private const val TAG = "NotifRankingManager"
|
||||
|
||||
@@ -60,10 +61,11 @@ open class NotificationRankingManager @Inject constructor(
|
||||
private val logger: NotificationEntryManagerLogger,
|
||||
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
|
||||
private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
|
||||
private val highPriorityProvider: HighPriorityProvider
|
||||
) {
|
||||
private val highPriorityProvider: HighPriorityProvider,
|
||||
private val keyguardEnvironment: KeyguardEnvironment
|
||||
) : LegacyNotificationRanker {
|
||||
|
||||
var rankingMap: RankingMap? = null
|
||||
override var rankingMap: RankingMap? = null
|
||||
protected set
|
||||
private val mediaManager by lazy {
|
||||
mediaManagerLazy.get()
|
||||
@@ -115,7 +117,7 @@ open class NotificationRankingManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRanking(
|
||||
override fun updateRanking(
|
||||
newRankingMap: RankingMap?,
|
||||
entries: Collection<NotificationEntry>,
|
||||
reason: String
|
||||
@@ -131,6 +133,12 @@ open class NotificationRankingManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun isNotificationForCurrentProfiles(
|
||||
entry: NotificationEntry
|
||||
): Boolean {
|
||||
return keyguardEnvironment.isNotificationForCurrentProfiles(entry.sbn)
|
||||
}
|
||||
|
||||
/** Uses the [rankingComparator] to sort notifications which aren't filtered */
|
||||
private fun filterAndSortLocked(
|
||||
entries: Collection<NotificationEntry>,
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection.coordinator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
|
||||
|
||||
@@ -27,5 +29,5 @@ public interface Coordinator {
|
||||
* Called after the NewNotifPipeline is initialized.
|
||||
* Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
|
||||
*/
|
||||
void attach(NotifPipeline pipeline);
|
||||
void attach(@NonNull NotifPipeline pipeline);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public class NotifCoordinators implements Dumpable {
|
||||
ConversationCoordinator conversationCoordinator,
|
||||
PreparationCoordinator preparationCoordinator,
|
||||
MediaCoordinator mediaCoordinator,
|
||||
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
|
||||
VisualStabilityCoordinator visualStabilityCoordinator) {
|
||||
dumpManager.registerDumpable(TAG, this);
|
||||
|
||||
@@ -70,9 +71,14 @@ public class NotifCoordinators implements Dumpable {
|
||||
mCoordinators.add(appOpsCoordinator);
|
||||
mCoordinators.add(deviceProvisionedCoordinator);
|
||||
mCoordinators.add(bubbleCoordinator);
|
||||
mCoordinators.add(mediaCoordinator);
|
||||
mCoordinators.add(conversationCoordinator);
|
||||
mCoordinators.add(mediaCoordinator);
|
||||
mCoordinators.add(visualStabilityCoordinator);
|
||||
|
||||
if (featureFlags.isSmartspaceDedupingEnabled()) {
|
||||
mCoordinators.add(smartspaceDedupingCoordinator);
|
||||
}
|
||||
|
||||
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
|
||||
mCoordinators.add(headsUpCoordinator);
|
||||
mCoordinators.add(preparationCoordinator);
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.coordinator
|
||||
|
||||
import android.app.smartspace.SmartspaceTarget
|
||||
import android.os.Parcelable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager
|
||||
import com.android.systemui.statusbar.StatusBarState
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
||||
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.time.SystemClock
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Hides notifications on the lockscreen if the content of those notifications is also visible
|
||||
* in smartspace. This ONLY hides the notifications on the lockscreen: if the user pulls the shade
|
||||
* down or unlocks the device, then the notifications are unhidden.
|
||||
*
|
||||
* In addition, notifications that have recently alerted aren't filtered. Tracking this in a way
|
||||
* that involves the fewest pipeline invalidations requires some unfortunately complex logic.
|
||||
*/
|
||||
// This class is a singleton so that the same instance can be accessed by both the old and new
|
||||
// pipelines
|
||||
@SysUISingleton
|
||||
class SmartspaceDedupingCoordinator @Inject constructor(
|
||||
private val statusBarStateController: SysuiStatusBarStateController,
|
||||
private val smartspaceController: LockscreenSmartspaceController,
|
||||
private val notificationEntryManager: NotificationEntryManager,
|
||||
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
|
||||
private val notifPipeline: NotifPipeline,
|
||||
@Main private val executor: DelayableExecutor,
|
||||
private val clock: SystemClock
|
||||
) : Coordinator {
|
||||
private var isOnLockscreen = false
|
||||
|
||||
private var trackedSmartspaceTargets = mutableMapOf<String, TrackedSmartspaceTarget>()
|
||||
|
||||
override fun attach(pipeline: NotifPipeline) {
|
||||
pipeline.addPreGroupFilter(filter)
|
||||
pipeline.addCollectionListener(collectionListener)
|
||||
statusBarStateController.addCallback(statusBarStateListener)
|
||||
smartspaceController.addListener(this::onNewSmartspaceTargets)
|
||||
|
||||
// TODO (b/173126564): Remove this once the old pipeline is no longer necessary
|
||||
notificationLockscreenUserManager.addKeyguardNotificationSuppressor { entry ->
|
||||
isDupedWithSmartspaceContent(entry)
|
||||
}
|
||||
|
||||
recordStatusBarState(statusBarStateController.state)
|
||||
}
|
||||
|
||||
private fun isDupedWithSmartspaceContent(entry: NotificationEntry): Boolean {
|
||||
return trackedSmartspaceTargets[entry.key]?.shouldFilter ?: false
|
||||
}
|
||||
|
||||
private val filter = object : NotifFilter("SmartspaceDedupingFilter") {
|
||||
override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
|
||||
return isOnLockscreen && isDupedWithSmartspaceContent(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private val collectionListener = object : NotifCollectionListener {
|
||||
override fun onEntryAdded(entry: NotificationEntry) {
|
||||
trackedSmartspaceTargets[entry.key]?.let {
|
||||
updateFilterStatus(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEntryUpdated(entry: NotificationEntry) {
|
||||
trackedSmartspaceTargets[entry.key]?.let {
|
||||
updateFilterStatus(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
|
||||
trackedSmartspaceTargets[entry.key]?.let { trackedTarget ->
|
||||
cancelExceptionTimeout(trackedTarget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val statusBarStateListener = object : StatusBarStateController.StateListener {
|
||||
override fun onStateChanged(newState: Int) {
|
||||
recordStatusBarState(newState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onNewSmartspaceTargets(targets: List<Parcelable>) {
|
||||
var changed = false
|
||||
val newMap = mutableMapOf<String, TrackedSmartspaceTarget>()
|
||||
val oldMap = trackedSmartspaceTargets
|
||||
|
||||
for (target in targets) {
|
||||
// For all targets that are SmartspaceTargets and have non-null sourceNotificationKeys
|
||||
(target as? SmartspaceTarget)?.sourceNotificationKey?.let { key ->
|
||||
val trackedTarget = oldMap.getOrElse(key) {
|
||||
TrackedSmartspaceTarget(key)
|
||||
}
|
||||
newMap[key] = trackedTarget
|
||||
changed = changed || updateFilterStatus(trackedTarget)
|
||||
}
|
||||
// Currently, only filter out the first target
|
||||
break
|
||||
}
|
||||
|
||||
for (prevKey in oldMap.keys) {
|
||||
if (!newMap.containsKey(prevKey)) {
|
||||
oldMap[prevKey]?.cancelTimeoutRunnable?.run()
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
filter.invalidateList()
|
||||
notificationEntryManager.updateNotifications("Smartspace targets changed")
|
||||
}
|
||||
|
||||
trackedSmartspaceTargets = newMap
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the target's alert exception status has changed
|
||||
*/
|
||||
private fun updateFilterStatus(target: TrackedSmartspaceTarget): Boolean {
|
||||
val prevShouldFilter = target.shouldFilter
|
||||
|
||||
val entry = notifPipeline.getEntry(target.key)
|
||||
if (entry != null) {
|
||||
updateAlertException(target, entry)
|
||||
|
||||
target.shouldFilter = !hasRecentlyAlerted(entry)
|
||||
}
|
||||
|
||||
return target.shouldFilter != prevShouldFilter && isOnLockscreen
|
||||
}
|
||||
|
||||
private fun updateAlertException(target: TrackedSmartspaceTarget, entry: NotificationEntry) {
|
||||
val now = clock.currentTimeMillis()
|
||||
val alertExceptionExpires = entry.ranking.lastAudiblyAlertedMillis + ALERT_WINDOW
|
||||
|
||||
if (alertExceptionExpires != target.alertExceptionExpires &&
|
||||
alertExceptionExpires > now) {
|
||||
// If we got here, the target is subject to a new alert exception window, so we
|
||||
// should update our timeout to fire at the end of the new window
|
||||
|
||||
target.cancelTimeoutRunnable?.run()
|
||||
target.alertExceptionExpires = alertExceptionExpires
|
||||
target.cancelTimeoutRunnable = executor.executeDelayed({
|
||||
target.cancelTimeoutRunnable = null
|
||||
target.shouldFilter = true
|
||||
filter.invalidateList()
|
||||
notificationEntryManager.updateNotifications("deduping timeout expired")
|
||||
}, alertExceptionExpires - now)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelExceptionTimeout(target: TrackedSmartspaceTarget) {
|
||||
target.cancelTimeoutRunnable?.run()
|
||||
target.cancelTimeoutRunnable = null
|
||||
target.alertExceptionExpires = 0
|
||||
}
|
||||
|
||||
private fun recordStatusBarState(newState: Int) {
|
||||
val wasOnLockscreen = isOnLockscreen
|
||||
isOnLockscreen = newState == StatusBarState.KEYGUARD
|
||||
|
||||
if (isOnLockscreen != wasOnLockscreen) {
|
||||
filter.invalidateList()
|
||||
// No need to call notificationEntryManager.updateNotifications; something else already
|
||||
// does it for us when the keyguard state changes
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasRecentlyAlerted(entry: NotificationEntry): Boolean {
|
||||
return clock.currentTimeMillis() - entry.ranking.lastAudiblyAlertedMillis <= ALERT_WINDOW
|
||||
}
|
||||
}
|
||||
|
||||
private class TrackedSmartspaceTarget(
|
||||
val key: String
|
||||
) {
|
||||
var cancelTimeoutRunnable: Runnable? = null
|
||||
var alertExceptionExpires: Long = 0
|
||||
var shouldFilter = false
|
||||
}
|
||||
|
||||
private val ALERT_WINDOW = SECONDS.toMillis(30)
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.service.notification.NotificationListenerService
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
|
||||
interface LegacyNotificationRanker {
|
||||
val rankingMap: NotificationListenerService.RankingMap?
|
||||
|
||||
fun updateRanking(
|
||||
newRankingMap: NotificationListenerService.RankingMap?,
|
||||
entries: Collection<NotificationEntry>,
|
||||
reason: String
|
||||
): List<NotificationEntry>
|
||||
|
||||
fun isNotificationForCurrentProfiles(
|
||||
entry: NotificationEntry
|
||||
): Boolean
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.service.notification.NotificationListenerService.Ranking;
|
||||
import android.service.notification.NotificationListenerService.RankingMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Stub implementation that we use until we get passed the "real" one in the form of
|
||||
* {@link com.android.systemui.statusbar.notification.collection.NotificationRankingManager}
|
||||
*/
|
||||
public class LegacyNotificationRankerStub implements LegacyNotificationRanker {
|
||||
private RankingMap mRankingMap = new RankingMap(new Ranking[] {});
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<NotificationEntry> updateRanking(
|
||||
@Nullable RankingMap newRankingMap,
|
||||
@NonNull Collection<NotificationEntry> entries,
|
||||
@NonNull String reason) {
|
||||
if (newRankingMap != null) {
|
||||
mRankingMap = newRankingMap;
|
||||
}
|
||||
List<NotificationEntry> ranked = new ArrayList<>(entries);
|
||||
ranked.sort(mEntryComparator);
|
||||
return ranked;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RankingMap getRankingMap() {
|
||||
return mRankingMap;
|
||||
}
|
||||
|
||||
private final Comparator<NotificationEntry> mEntryComparator = Comparator.comparingLong(
|
||||
o -> o.getSbn().getNotification().when);
|
||||
|
||||
@Override
|
||||
public boolean isNotificationForCurrentProfiles(@NonNull NotificationEntry entry) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
|
||||
@@ -45,5 +47,5 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
|
||||
* various entries against.
|
||||
* @return True if the notif should be removed from the list
|
||||
*/
|
||||
public abstract boolean shouldFilterOut(NotificationEntry entry, long now);
|
||||
public abstract boolean shouldFilterOut(@NonNull NotificationEntry entry, long now);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManagerLogge
|
||||
import com.android.systemui.statusbar.notification.collection.NotifCollection;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
|
||||
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
|
||||
@@ -100,8 +99,6 @@ public interface NotificationsModule {
|
||||
static NotificationEntryManager provideNotificationEntryManager(
|
||||
NotificationEntryManagerLogger logger,
|
||||
NotificationGroupManagerLegacy groupManager,
|
||||
Lazy<NotificationRankingManager> rankingManager,
|
||||
NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
|
||||
FeatureFlags featureFlags,
|
||||
Lazy<NotificationRowBinder> notificationRowBinderLazy,
|
||||
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
|
||||
@@ -111,8 +108,6 @@ public interface NotificationsModule {
|
||||
return new NotificationEntryManager(
|
||||
logger,
|
||||
groupManager,
|
||||
rankingManager,
|
||||
keyguardEnvironment,
|
||||
featureFlags,
|
||||
notificationRowBinderLazy,
|
||||
notificationRemoteInputManagerLazy,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.init
|
||||
|
||||
import android.content.Context
|
||||
import android.service.notification.StatusBarNotification
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
|
||||
@@ -30,6 +29,7 @@ import com.android.systemui.statusbar.notification.NotificationClicker
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager
|
||||
import com.android.systemui.statusbar.notification.NotificationListController
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
|
||||
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
|
||||
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
|
||||
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
|
||||
@@ -59,10 +59,10 @@ import javax.inject.Inject
|
||||
*/
|
||||
@SysUISingleton
|
||||
class NotificationsControllerImpl @Inject constructor(
|
||||
private val context: Context,
|
||||
private val featureFlags: FeatureFlags,
|
||||
private val notificationListener: NotificationListener,
|
||||
private val entryManager: NotificationEntryManager,
|
||||
private val legacyRanker: NotificationRankingManager,
|
||||
private val notifPipeline: Lazy<NotifPipeline>,
|
||||
private val targetSdkResolver: TargetSdkResolver,
|
||||
private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
|
||||
@@ -128,6 +128,7 @@ class NotificationsControllerImpl @Inject constructor(
|
||||
groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
|
||||
groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
|
||||
|
||||
entryManager.setRanker(legacyRanker)
|
||||
entryManager.attach(notificationListener)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.content.Intent.ACTION_USER_SWITCHED;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_ALERTING;
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_MEDIA_CONTROLS;
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE;
|
||||
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
|
||||
@@ -55,6 +57,7 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager.KeyguardNotificationSuppressor;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
@@ -383,13 +386,44 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
|
||||
assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyguardNotificationSuppressors() {
|
||||
// GIVEN a notification that should be shown on the lockscreen
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
|
||||
final NotificationEntry entry = new NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_HIGH)
|
||||
.build();
|
||||
entry.setBucket(BUCKET_ALERTING);
|
||||
|
||||
// WHEN a suppressor is added that filters out all entries
|
||||
FakeKeyguardSuppressor suppressor = new FakeKeyguardSuppressor();
|
||||
mLockscreenUserManager.addKeyguardNotificationSuppressor(suppressor);
|
||||
|
||||
// THEN it's filtered out
|
||||
assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
|
||||
|
||||
// WHEN the suppressor no longer filters out entries
|
||||
suppressor.setShouldSuppress(false);
|
||||
|
||||
// THEN it's no longer filtered out
|
||||
assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
|
||||
}
|
||||
|
||||
private class TestNotificationLockscreenUserManager
|
||||
extends NotificationLockscreenUserManagerImpl {
|
||||
public TestNotificationLockscreenUserManager(Context context) {
|
||||
super(context, mBroadcastDispatcher, mDevicePolicyManager, mUserManager,
|
||||
mClickNotifier, NotificationLockscreenUserManagerTest.this.mKeyguardManager,
|
||||
mStatusBarStateController, Handler.createAsync(Looper.myLooper()),
|
||||
mDeviceProvisionedController, mKeyguardStateController);
|
||||
super(
|
||||
context,
|
||||
mBroadcastDispatcher,
|
||||
mDevicePolicyManager,
|
||||
mUserManager,
|
||||
mClickNotifier,
|
||||
NotificationLockscreenUserManagerTest.this.mKeyguardManager,
|
||||
mStatusBarStateController,
|
||||
Handler.createAsync(Looper.myLooper()),
|
||||
mDeviceProvisionedController,
|
||||
mKeyguardStateController);
|
||||
}
|
||||
|
||||
public BroadcastReceiver getBaseBroadcastReceiverForTest() {
|
||||
@@ -404,4 +438,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
|
||||
return mSettingsObserver;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FakeKeyguardSuppressor implements KeyguardNotificationSuppressor {
|
||||
private boolean mShouldSuppress = true;
|
||||
|
||||
@Override
|
||||
public boolean shouldSuppressOnKeyguard(NotificationEntry entry) {
|
||||
return mShouldSuppress;
|
||||
}
|
||||
|
||||
public void setShouldSuppress(boolean shouldSuppress) {
|
||||
mShouldSuppress = shouldSuppress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,16 +190,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
mEntryManager = new NotificationEntryManager(
|
||||
mLogger,
|
||||
mGroupManager,
|
||||
() -> new NotificationRankingManager(
|
||||
() -> mNotificationMediaManager,
|
||||
mGroupManager,
|
||||
mHeadsUpManager,
|
||||
mock(NotificationFilter.class),
|
||||
mLogger,
|
||||
mock(NotificationSectionsFeatureManager.class),
|
||||
mock(PeopleNotificationIdentifier.class),
|
||||
mock(HighPriorityProvider.class)),
|
||||
mEnvironment,
|
||||
mFeatureFlags,
|
||||
() -> mNotificationRowBinder,
|
||||
() -> mRemoteInputManager,
|
||||
@@ -207,6 +197,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
mock(ForegroundServiceDismissalFeatureController.class),
|
||||
mock(IStatusBarService.class)
|
||||
);
|
||||
mEntryManager.setRanker(
|
||||
new NotificationRankingManager(
|
||||
() -> mNotificationMediaManager,
|
||||
mGroupManager,
|
||||
mHeadsUpManager,
|
||||
mock(NotificationFilter.class),
|
||||
mLogger,
|
||||
mock(NotificationSectionsFeatureManager.class),
|
||||
mock(PeopleNotificationIdentifier.class),
|
||||
mock(HighPriorityProvider.class),
|
||||
mEnvironment));
|
||||
mEntryManager.setUpWithPresenter(mPresenter);
|
||||
mEntryManager.addNotificationEntryListener(mEntryListener);
|
||||
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
|
||||
|
||||
@@ -77,13 +77,16 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
mock(StatusBarNotification.class);
|
||||
|
||||
@Mock
|
||||
ForegroundServiceController mFsc;
|
||||
StatusBarStateController mStatusBarStateController;
|
||||
@Mock
|
||||
KeyguardEnvironment mEnvironment;
|
||||
@Mock
|
||||
MediaFeatureFlag mMediaFeatureFlag;
|
||||
ForegroundServiceController mFsc;
|
||||
@Mock
|
||||
StatusBarStateController mStatusBarStateController;
|
||||
NotificationLockscreenUserManager mUserManager;
|
||||
@Mock
|
||||
MediaFeatureFlag mMediaFeatureFlag;
|
||||
|
||||
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
|
||||
|
||||
private NotificationFilter mNotificationFilter;
|
||||
@@ -127,7 +130,12 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
mDependency,
|
||||
TestableLooper.get(this));
|
||||
mRow = testHelper.createRow();
|
||||
mNotificationFilter = new NotificationFilter(mStatusBarStateController, mMediaFeatureFlag);
|
||||
mNotificationFilter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -195,7 +203,11 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterOtherNotificationWhenDisabled() {
|
||||
// GIVEN that the media feature is disabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
|
||||
NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
// WHEN the media filter is asked about an entry
|
||||
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
|
||||
@@ -208,7 +220,11 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterOtherNotificationWhenEnabled() {
|
||||
// GIVEN that the media feature is enabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
|
||||
NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
// WHEN the media filter is asked about an entry
|
||||
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
|
||||
@@ -221,7 +237,11 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterMediaNotificationWhenDisabled() {
|
||||
// GIVEN that the media feature is disabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
|
||||
NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
// WHEN the media filter is asked about a media entry
|
||||
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
|
||||
@@ -233,7 +253,11 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterMediaNotificationWhenEnabled() {
|
||||
// GIVEN that the media feature is enabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
|
||||
NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
// WHEN the media filter is asked about a media entry
|
||||
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
|
||||
|
||||
@@ -30,9 +30,11 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
|
||||
import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
|
||||
import com.android.systemui.statusbar.notification.NotificationFilter
|
||||
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
|
||||
@@ -42,7 +44,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.Lazy
|
||||
@@ -79,9 +80,11 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
mock(NotificationEntryManagerLogger::class.java),
|
||||
sectionsManager,
|
||||
personNotificationIdentifier,
|
||||
HighPriorityProvider(personNotificationIdentifier,
|
||||
mock(NotificationGroupManagerLegacy::class.java))
|
||||
)
|
||||
HighPriorityProvider(
|
||||
personNotificationIdentifier,
|
||||
mock(NotificationGroupManagerLegacy::class.java)),
|
||||
mock(KeyguardEnvironment::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -486,7 +489,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
logger: NotificationEntryManagerLogger,
|
||||
sectionsFeatureManager: NotificationSectionsFeatureManager,
|
||||
peopleNotificationIdentifier: PeopleNotificationIdentifier,
|
||||
highPriorityProvider: HighPriorityProvider
|
||||
highPriorityProvider: HighPriorityProvider,
|
||||
keyguardEnvironment: KeyguardEnvironment
|
||||
) : NotificationRankingManager(
|
||||
mediaManager,
|
||||
groupManager,
|
||||
@@ -495,7 +499,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
logger,
|
||||
sectionsFeatureManager,
|
||||
peopleNotificationIdentifier,
|
||||
highPriorityProvider
|
||||
highPriorityProvider,
|
||||
keyguardEnvironment
|
||||
) {
|
||||
fun applyTestRankingMap(r: RankingMap) {
|
||||
rankingMap = r
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.coordinator
|
||||
|
||||
import android.app.smartspace.SmartspaceTarget
|
||||
import android.content.ComponentName
|
||||
import android.os.UserHandle
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager
|
||||
import com.android.systemui.statusbar.StatusBarState
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
||||
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager
|
||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.anyString
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@SmallTest
|
||||
class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var statusBarStateController: SysuiStatusBarStateController
|
||||
@Mock
|
||||
private lateinit var smartspaceController: LockscreenSmartspaceController
|
||||
@Mock
|
||||
private lateinit var notificationEntryManager: NotificationEntryManager
|
||||
@Mock
|
||||
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
|
||||
@Mock
|
||||
private lateinit var notifPipeline: NotifPipeline
|
||||
@Mock
|
||||
private lateinit var pluggableListener: Pluggable.PluggableListener<NotifFilter>
|
||||
|
||||
@Captor
|
||||
private lateinit var filterCaptor: ArgumentCaptor<NotifFilter>
|
||||
@Captor
|
||||
private lateinit var collectionListenerCaptor: ArgumentCaptor<NotifCollectionListener>
|
||||
@Captor
|
||||
private lateinit var stateListenerCaptor: ArgumentCaptor<StatusBarStateController.StateListener>
|
||||
@Captor
|
||||
private lateinit var smartspaceListenerCaptor: ArgumentCaptor<SmartspaceTargetListener>
|
||||
|
||||
private lateinit var filter: NotifFilter
|
||||
private lateinit var collectionListener: NotifCollectionListener
|
||||
private lateinit var statusBarListener: StatusBarStateController.StateListener
|
||||
private lateinit var newTargetListener: SmartspaceTargetListener
|
||||
|
||||
private lateinit var entry1HasRecentlyAlerted: NotificationEntry
|
||||
private lateinit var entry2HasNotRecentlyAlerted: NotificationEntry
|
||||
private lateinit var entry3NotAssociatedWithTarget: NotificationEntry
|
||||
private lateinit var entry4HasNotRecentlyAlerted: NotificationEntry
|
||||
private lateinit var target1: SmartspaceTarget
|
||||
private lateinit var target2: SmartspaceTarget
|
||||
private lateinit var target4: SmartspaceTarget
|
||||
|
||||
private val clock = FakeSystemClock()
|
||||
private val executor = FakeExecutor(clock)
|
||||
private val now = clock.currentTimeMillis()
|
||||
|
||||
private lateinit var deduper: SmartspaceDedupingCoordinator
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
// Mock out some behavior
|
||||
`when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
|
||||
|
||||
// Build the deduper
|
||||
deduper = SmartspaceDedupingCoordinator(
|
||||
statusBarStateController,
|
||||
smartspaceController,
|
||||
notificationEntryManager,
|
||||
notificationLockscreenUserManager,
|
||||
notifPipeline,
|
||||
executor,
|
||||
clock
|
||||
)
|
||||
|
||||
// Attach the deduper and capture the listeners/filters that it registers
|
||||
deduper.attach(notifPipeline)
|
||||
|
||||
verify(notifPipeline).addPreGroupFilter(filterCaptor.capture())
|
||||
filter = filterCaptor.value
|
||||
filter.setInvalidationListener(pluggableListener)
|
||||
|
||||
verify(notifPipeline).addCollectionListener(capture(collectionListenerCaptor))
|
||||
collectionListener = collectionListenerCaptor.value
|
||||
|
||||
verify(statusBarStateController).addCallback(capture(stateListenerCaptor))
|
||||
statusBarListener = stateListenerCaptor.value
|
||||
|
||||
verify(smartspaceController).addListener(capture(smartspaceListenerCaptor))
|
||||
newTargetListener = smartspaceListenerCaptor.value
|
||||
|
||||
// Initialize some test data
|
||||
entry1HasRecentlyAlerted = NotificationEntryBuilder()
|
||||
.setPkg(PACKAGE_1)
|
||||
.setId(11)
|
||||
.setLastAudiblyAlertedMs(now - 10000)
|
||||
.build()
|
||||
entry2HasNotRecentlyAlerted = NotificationEntryBuilder()
|
||||
.setPkg(PACKAGE_2)
|
||||
.setId(22)
|
||||
.build()
|
||||
entry3NotAssociatedWithTarget = NotificationEntryBuilder()
|
||||
.setPkg("com.test.package.3")
|
||||
.setId(33)
|
||||
.setLastAudiblyAlertedMs(now - 10000)
|
||||
.build()
|
||||
entry4HasNotRecentlyAlerted = NotificationEntryBuilder()
|
||||
.setPkg(PACKAGE_2)
|
||||
.setId(44)
|
||||
.build()
|
||||
|
||||
target1 = buildTargetFor(entry1HasRecentlyAlerted)
|
||||
target2 = buildTargetFor(entry2HasNotRecentlyAlerted)
|
||||
target4 = buildTargetFor(entry4HasNotRecentlyAlerted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBasicFiltering() {
|
||||
// GIVEN a few notifications
|
||||
addEntries(
|
||||
entry2HasNotRecentlyAlerted,
|
||||
entry3NotAssociatedWithTarget,
|
||||
entry4HasNotRecentlyAlerted)
|
||||
|
||||
// WHEN we receive smartspace targets associated with entry 2 and 3
|
||||
sendTargets(target2, target4)
|
||||
|
||||
// THEN both pipelines are rerun
|
||||
verifyPipelinesInvalidated()
|
||||
|
||||
// THEN the first target is filtered out, but the other ones aren't
|
||||
assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
assertFalse(filter.shouldFilterOut(entry3NotAssociatedWithTarget, now))
|
||||
assertFalse(filter.shouldFilterOut(entry4HasNotRecentlyAlerted, now))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoNotFilterRecentlyAlertedNotifs() {
|
||||
// GIVEN one notif that recently alerted and a second that hasn't
|
||||
addEntries(entry1HasRecentlyAlerted, entry2HasNotRecentlyAlerted)
|
||||
|
||||
// WHEN they become associated with smartspace targets
|
||||
sendTargets(target1, target2)
|
||||
|
||||
// THEN neither is filtered (the first because it's recently alerted and the second
|
||||
// because it's not in the first position
|
||||
verifyPipelinesNotInvalidated()
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
|
||||
assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFilterAlertedButNotRecentNotifs() {
|
||||
// GIVEN a notification that alerted, but a very long time ago
|
||||
val entryOldAlert = NotificationEntryBuilder(entry1HasRecentlyAlerted)
|
||||
.setLastAudiblyAlertedMs(now - 40000)
|
||||
.build()
|
||||
addEntries(entryOldAlert)
|
||||
|
||||
// WHEN it becomes part of smartspace
|
||||
val target = buildTargetFor(entryOldAlert)
|
||||
sendTargets(target)
|
||||
|
||||
// THEN it's still filtered out (because it's not in the alert window)
|
||||
verifyPipelinesInvalidated()
|
||||
assertTrue(filter.shouldFilterOut(entryOldAlert, now))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExceptionExpires() {
|
||||
// GIVEN a recently-alerted notif that is the primary smartspace target
|
||||
addEntries(entry1HasRecentlyAlerted)
|
||||
sendTargets(target1)
|
||||
clearPipelineInvocations()
|
||||
|
||||
// WHEN we go beyond the target's exception window
|
||||
clock.advanceTime(20000)
|
||||
|
||||
// THEN the pipeline is invalidated
|
||||
verifyPipelinesInvalidated()
|
||||
assertExecutorIsClear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExceptionIsEventuallyFiltered() {
|
||||
// GIVEN a notif that has recently alerted
|
||||
addEntries(entry1HasRecentlyAlerted)
|
||||
|
||||
// WHEN it becomes the primary smartspace target
|
||||
sendTargets(target1)
|
||||
|
||||
// THEN it isn't filtered out (because it recently alerted)
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
|
||||
|
||||
// WHEN we pass the alert window
|
||||
clock.advanceTime(20000)
|
||||
|
||||
// THEN the notif is once again filtered
|
||||
assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExceptionIsUpdated() {
|
||||
// GIVEN a notif that has recently alerted and is the primary smartspace target
|
||||
addEntries(entry1HasRecentlyAlerted)
|
||||
sendTargets(target1)
|
||||
clearPipelineInvocations()
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
|
||||
// GIVEN the notif is updated with a much more recent alert time
|
||||
NotificationEntryBuilder(entry1HasRecentlyAlerted)
|
||||
.setLastAudiblyAlertedMs(clock.currentTimeMillis() - 500)
|
||||
.apply(entry1HasRecentlyAlerted)
|
||||
updateEntries(entry1HasRecentlyAlerted)
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
|
||||
// WHEN we advance beyond the original exception window
|
||||
clock.advanceTime(25000)
|
||||
|
||||
// THEN the original exception window doesn't fire
|
||||
verifyPipelinesNotInvalidated()
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
|
||||
// WHEN we advance beyond the new exception window
|
||||
clock.advanceTime(4500)
|
||||
|
||||
// THEN the pipelines are invalidated and no more timeouts are scheduled
|
||||
verifyPipelinesInvalidated()
|
||||
assertExecutorIsClear()
|
||||
assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReplacementIsCanceled() {
|
||||
// GIVEN a single notif and smartspace target
|
||||
addEntries(entry1HasRecentlyAlerted)
|
||||
sendTargets(target1)
|
||||
clearPipelineInvocations()
|
||||
|
||||
// WHEN a higher-ranked target arrives
|
||||
val newerEntry = NotificationEntryBuilder()
|
||||
.setPkg(PACKAGE_2)
|
||||
.setId(55)
|
||||
.setLastAudiblyAlertedMs(now - 1000)
|
||||
.build()
|
||||
val newerTarget = buildTargetFor(newerEntry)
|
||||
sendTargets(newerTarget, target1)
|
||||
|
||||
// THEN the timeout of the other target is canceled and it is no longer filtered
|
||||
assertExecutorIsClear()
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis()))
|
||||
verifyPipelinesInvalidated()
|
||||
clearPipelineInvocations()
|
||||
|
||||
// WHEN the entry associated with the newer target later arrives
|
||||
addEntries(newerEntry)
|
||||
|
||||
// THEN the entry is not filtered out (because it recently alerted)
|
||||
assertFalse(filter.shouldFilterOut(newerEntry, clock.uptimeMillis()))
|
||||
|
||||
// WHEN its exception window passes
|
||||
clock.advanceTime(ALERT_WINDOW)
|
||||
|
||||
// THEN we go back to filtering it
|
||||
verifyPipelinesInvalidated()
|
||||
assertExecutorIsClear()
|
||||
assertTrue(filter.shouldFilterOut(newerEntry, clock.uptimeMillis()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRetractedIsCanceled() {
|
||||
// GIVEN A recently alerted target
|
||||
addEntries(entry1HasRecentlyAlerted)
|
||||
sendTargets(target1)
|
||||
|
||||
// WHEN the entry is removed
|
||||
removeEntries(entry1HasRecentlyAlerted)
|
||||
|
||||
// THEN its pending timeout is canceled
|
||||
assertExecutorIsClear()
|
||||
clock.advanceTime(ALERT_WINDOW)
|
||||
verifyPipelinesNotInvalidated()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTargetBeforeEntryFunctionsProperly() {
|
||||
// WHEN targets are added before their entries exist
|
||||
sendTargets(target2, target1)
|
||||
|
||||
// THEN neither is filtered out
|
||||
assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now))
|
||||
|
||||
// WHEN the entries are later added
|
||||
addEntries(entry2HasNotRecentlyAlerted, entry1HasRecentlyAlerted)
|
||||
|
||||
// THEN the pipelines are not invalidated (because they're already going to be rerun)
|
||||
// but the first entry is still filtered out properly.
|
||||
verifyPipelinesNotInvalidated()
|
||||
assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLockscreenTracking() {
|
||||
// GIVEN a couple of smartspace targets that haven't alerted recently
|
||||
addEntries(entry2HasNotRecentlyAlerted, entry4HasNotRecentlyAlerted)
|
||||
sendTargets(target2, target4)
|
||||
clearPipelineInvocations()
|
||||
|
||||
assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
|
||||
// WHEN we are no longer on the keyguard
|
||||
statusBarListener.onStateChanged(StatusBarState.SHADE)
|
||||
|
||||
// THEN the new pipeline is invalidated (but the old one isn't because it's not
|
||||
// necessary) because the notif should no longer be filtered out
|
||||
verify(pluggableListener).onPluggableInvalidated(filter)
|
||||
verify(notificationEntryManager, never()).updateNotifications(anyString())
|
||||
assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
|
||||
}
|
||||
|
||||
private fun buildTargetFor(entry: NotificationEntry): SmartspaceTarget {
|
||||
return SmartspaceTarget
|
||||
.Builder("test", ComponentName("test", "class"), UserHandle.CURRENT)
|
||||
.setSourceNotificationKey(entry.key)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun addEntries(vararg entries: NotificationEntry) {
|
||||
for (entry in entries) {
|
||||
`when`(notifPipeline.getEntry(entry.key)).thenReturn(entry)
|
||||
collectionListener.onEntryAdded(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateEntries(vararg entries: NotificationEntry) {
|
||||
for (entry in entries) {
|
||||
`when`(notifPipeline.getEntry(entry.key)).thenReturn(entry)
|
||||
collectionListener.onEntryUpdated(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeEntries(vararg entries: NotificationEntry) {
|
||||
for (entry in entries) {
|
||||
`when`(notifPipeline.getEntry(entry.key)).thenReturn(null)
|
||||
collectionListener.onEntryRemoved(entry, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTargets(vararg targets: SmartspaceTarget) {
|
||||
newTargetListener.onSmartspaceTargetsUpdated(targets.toMutableList())
|
||||
}
|
||||
|
||||
private fun verifyPipelinesInvalidated() {
|
||||
verify(pluggableListener).onPluggableInvalidated(filter)
|
||||
verify(notificationEntryManager).updateNotifications(anyString())
|
||||
}
|
||||
|
||||
private fun assertExecutorIsClear() {
|
||||
assertEquals(0, executor.numPending())
|
||||
}
|
||||
|
||||
private fun verifyPipelinesNotInvalidated() {
|
||||
verify(pluggableListener, never()).onPluggableInvalidated(filter)
|
||||
verify(notificationEntryManager, never()).updateNotifications(anyString())
|
||||
}
|
||||
|
||||
private fun clearPipelineInvocations() {
|
||||
clearInvocations(pluggableListener)
|
||||
clearInvocations(notificationEntryManager)
|
||||
}
|
||||
}
|
||||
|
||||
private val ALERT_WINDOW = TimeUnit.SECONDS.toMillis(30)
|
||||
private const val PACKAGE_1 = "com.test.package.1"
|
||||
private const val PACKAGE_2 = "com.test.package.2"
|
||||
@@ -181,16 +181,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
|
||||
mEntryManager = new NotificationEntryManager(
|
||||
mock(NotificationEntryManagerLogger.class),
|
||||
mGroupMembershipManager,
|
||||
() -> new NotificationRankingManager(
|
||||
() -> mock(NotificationMediaManager.class),
|
||||
mGroupMembershipManager,
|
||||
mHeadsUpManager,
|
||||
mock(NotificationFilter.class),
|
||||
mock(NotificationEntryManagerLogger.class),
|
||||
mock(NotificationSectionsFeatureManager.class),
|
||||
mock(PeopleNotificationIdentifier.class),
|
||||
mock(HighPriorityProvider.class)),
|
||||
mEnvironment,
|
||||
mFeatureFlags,
|
||||
() -> mRowBinder,
|
||||
() -> mRemoteInputManager,
|
||||
@@ -198,6 +188,17 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
|
||||
mock(ForegroundServiceDismissalFeatureController.class),
|
||||
mock(IStatusBarService.class)
|
||||
);
|
||||
mEntryManager.setRanker(
|
||||
new NotificationRankingManager(
|
||||
() -> mock(NotificationMediaManager.class),
|
||||
mGroupMembershipManager,
|
||||
mHeadsUpManager,
|
||||
mock(NotificationFilter.class),
|
||||
mock(NotificationEntryManagerLogger.class),
|
||||
mock(NotificationSectionsFeatureManager.class),
|
||||
mock(PeopleNotificationIdentifier.class),
|
||||
mock(HighPriorityProvider.class),
|
||||
mEnvironment));
|
||||
|
||||
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
|
||||
NotifBindPipeline pipeline = new NotifBindPipeline(
|
||||
|
||||
Reference in New Issue
Block a user