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:
Ned Burns
2021-05-13 20:29:31 +00:00
committed by Android (Google) Code Review
23 changed files with 946 additions and 115 deletions

View File

@@ -49,4 +49,6 @@
<bool name="flag_ongoing_call_status_bar_chip">true</bool> <bool name="flag_ongoing_call_status_bar_chip">true</bool>
<bool name="flag_smartspace">false</bool> <bool name="flag_smartspace">false</bool>
<bool name="flag_smartspace_deduping">true</bool>
</resources> </resources>

View File

@@ -92,4 +92,8 @@ public class FeatureFlags {
public boolean isSmartspaceEnabled() { public boolean isSmartspaceEnabled() {
return mFlagReader.isEnabled(R.bool.flag_smartspace); return mFlagReader.isEnabled(R.bool.flag_smartspace);
} }
public boolean isSmartspaceDedupingEnabled() {
return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
}
} }

View File

@@ -49,6 +49,12 @@ public interface NotificationLockscreenUserManager {
/** Adds a listener to be notified when the current user changes. */ /** Adds a listener to be notified when the current user changes. */
void addUserChangedListener(UserChangedListener listener); 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 * Removes a listener previously registered with
* {@link #addUserChangedListener(UserChangedListener)} * {@link #addUserChangedListener(UserChangedListener)}
@@ -88,4 +94,9 @@ public interface NotificationLockscreenUserManager {
default void onUserChanged(int userId) {} default void onUserChanged(int userId) {}
default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {} default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
} }
/** Used to hide notifications on the lockscreen */
interface KeyguardNotificationSuppressor {
boolean shouldSuppressOnKeyguard(NotificationEntry entry);
}
} }

View File

@@ -98,6 +98,7 @@ public class NotificationLockscreenUserManagerImpl implements
private LockPatternUtils mLockPatternUtils; private LockPatternUtils mLockPatternUtils;
protected KeyguardManager mKeyguardManager; protected KeyguardManager mKeyguardManager;
private int mState = StatusBarState.SHADE; private int mState = StatusBarState.SHADE;
private List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override @Override
@@ -343,6 +344,11 @@ public class NotificationLockscreenUserManagerImpl implements
Log.wtf(TAG, "mEntryManager was null!", new Throwable()); Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return false; return false;
} }
for (int i = 0; i < mKeyguardSuppressors.size(); i++) {
if (mKeyguardSuppressors.get(i).shouldSuppressOnKeyguard(entry)) {
return false;
}
}
boolean exceedsPriorityThreshold; boolean exceedsPriorityThreshold;
if (hideSilentNotificationsOnLockscreen()) { if (hideSilentNotificationsOnLockscreen()) {
exceedsPriorityThreshold = exceedsPriorityThreshold =
@@ -620,6 +626,11 @@ public class NotificationLockscreenUserManagerImpl implements
mListeners.add(listener); mListeners.add(listener);
} }
@Override
public void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor) {
mKeyguardSuppressors.add(suppressor);
}
@Override @Override
public void removeUserChangedListener(UserChangedListener listener) { public void removeUserChangedListener(UserChangedListener listener) {
mListeners.remove(listener); mListeners.remove(listener);

View File

@@ -46,8 +46,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.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.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; 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.notifcollection.CommonNotifCollection;
@@ -138,12 +139,11 @@ public class NotificationEntryManager implements
private final LeakDetector mLeakDetector; private final LeakDetector mLeakDetector;
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>(); private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManagerLegacy mGroupManager; private final NotificationGroupManagerLegacy mGroupManager;
private final Lazy<NotificationRankingManager> mRankingManager;
private final FeatureFlags mFeatureFlags; private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController; private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private LegacyNotificationRanker mRanker = new LegacyNotificationRankerStub();
private NotificationPresenter mPresenter; private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap; private RankingMap mLatestRankingMap;
@@ -200,8 +200,6 @@ public class NotificationEntryManager implements
public NotificationEntryManager( public NotificationEntryManager(
NotificationEntryManagerLogger logger, NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
Lazy<NotificationRankingManager> rankingManager,
KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags, FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
@@ -211,8 +209,6 @@ public class NotificationEntryManager implements
) { ) {
mLogger = logger; mLogger = logger;
mGroupManager = groupManager; mGroupManager = groupManager;
mRankingManager = rankingManager;
mKeyguardEnvironment = keyguardEnvironment;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mNotificationRowBinderLazy = notificationRowBinderLazy; mNotificationRowBinderLazy = notificationRowBinderLazy;
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy; mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
@@ -226,6 +222,10 @@ public class NotificationEntryManager implements
notificationListener.addNotificationHandler(mNotifListener); notificationListener.addNotificationHandler(mNotifListener);
} }
public void setRanker(LegacyNotificationRanker ranker) {
mRanker = ranker;
}
/** Adds a {@link NotificationEntryListener}. */ /** Adds a {@link NotificationEntryListener}. */
public void addNotificationEntryListener(NotificationEntryListener listener) { public void addNotificationEntryListener(NotificationEntryListener listener) {
mNotificationEntryListeners.add(listener); mNotificationEntryListeners.add(listener);
@@ -419,7 +419,7 @@ public class NotificationEntryManager implements
mActiveNotifications.put(entry.getKey(), entry); mActiveNotifications.put(entry.getKey(), entry);
mGroupManager.onEntryAdded(entry); mGroupManager.onEntryAdded(entry);
updateRankingAndSort(mRankingManager.get().getRankingMap(), "addEntryInternalInternal"); updateRankingAndSort(mRanker.getRankingMap(), "addEntryInternalInternal");
} }
/** /**
@@ -698,13 +698,6 @@ public class NotificationEntryManager implements
updateNotifications("updateNotificationInternal"); 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) { for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPostEntryUpdated(entry); listener.onPostEntryUpdated(entry);
} }
@@ -862,8 +855,7 @@ public class NotificationEntryManager implements
final int len = mActiveNotifications.size(); final int len = mActiveNotifications.size();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
NotificationEntry entry = mActiveNotifications.valueAt(i); NotificationEntry entry = mActiveNotifications.valueAt(i);
final StatusBarNotification sbn = entry.getSbn(); if (!mRanker.isNotificationForCurrentProfiles(entry)) {
if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
continue; continue;
} }
filtered.add(entry); filtered.add(entry);
@@ -886,13 +878,13 @@ public class NotificationEntryManager implements
/** Resorts / filters the current notification set with the current RankingMap */ /** Resorts / filters the current notification set with the current RankingMap */
public void reapplyFilterAndSort(String reason) { public void reapplyFilterAndSort(String reason) {
updateRankingAndSort(mRankingManager.get().getRankingMap(), reason); updateRankingAndSort(mRanker.getRankingMap(), reason);
} }
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */ /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) { private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
mSortedAndFiltered.clear(); mSortedAndFiltered.clear();
mSortedAndFiltered.addAll(mRankingManager.get().updateRanking( mSortedAndFiltered.addAll(mRanker.updateRanking(
rankingMap, mActiveNotifications.values(), reason)); rankingMap, mActiveNotifications.values(), reason));
} }

View File

@@ -27,14 +27,13 @@ import android.os.RemoteException;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController; import com.android.systemui.ForegroundServiceController;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager; 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.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import javax.inject.Inject; import javax.inject.Inject;
@@ -46,68 +45,43 @@ import javax.inject.Inject;
public class NotificationFilter { public class NotificationFilter {
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;
private final KeyguardEnvironment mKeyguardEnvironment;
private final ForegroundServiceController mForegroundServiceController;
private final NotificationLockscreenUserManager mUserManager;
private final Boolean mIsMediaFlagEnabled; private final Boolean mIsMediaFlagEnabled;
private NotificationEntryManager.KeyguardEnvironment mEnvironment;
private ShadeController mShadeController;
private ForegroundServiceController mFsc;
private NotificationLockscreenUserManager mUserManager;
@Inject @Inject
public NotificationFilter( public NotificationFilter(
StatusBarStateController statusBarStateController, StatusBarStateController statusBarStateController,
KeyguardEnvironment keyguardEnvironment,
ForegroundServiceController foregroundServiceController,
NotificationLockscreenUserManager userManager,
MediaFeatureFlag mediaFeatureFlag) { MediaFeatureFlag mediaFeatureFlag) {
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mKeyguardEnvironment = keyguardEnvironment;
mForegroundServiceController = foregroundServiceController;
mUserManager = userManager;
mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled(); 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. * @return true if the provided notification should NOT be shown right now.
*/ */
public boolean shouldFilterOut(NotificationEntry entry) { public boolean shouldFilterOut(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn(); final StatusBarNotification sbn = entry.getSbn();
if (!(getEnvironment().isDeviceProvisioned() if (!(mKeyguardEnvironment.isDeviceProvisioned()
|| showNotificationEvenIfUnprovisioned(sbn))) { || showNotificationEvenIfUnprovisioned(sbn))) {
return true; return true;
} }
if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
return true; return true;
} }
if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) if (mUserManager.isLockscreenPublicMode(sbn.getUserId())
&& (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
|| getUserManager().shouldHideNotifications(sbn.getUserId()) || mUserManager.shouldHideNotifications(sbn.getUserId())
|| getUserManager().shouldHideNotifications(sbn.getKey()))) { || mUserManager.shouldHideNotifications(sbn.getKey()))) {
return true; return true;
} }
@@ -123,8 +97,8 @@ public class NotificationFilter {
return true; return true;
} }
if (getFsc().isDisclosureNotification(sbn) if (mForegroundServiceController.isDisclosureNotification(sbn)
&& !getFsc().isDisclosureNeededForUser(sbn.getUserId())) { && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
// this is a foreground-service disclosure for a user that does not need to show one // this is a foreground-service disclosure for a user that does not need to show one
return true; return true;
} }

View File

@@ -185,6 +185,11 @@ public class NotifCollection implements Dumpable {
mBuildListener = buildListener; mBuildListener = buildListener;
} }
/** @see NotifPipeline#getEntry(String) () */
NotificationEntry getEntry(String key) {
return mNotificationSet.get(key);
}
/** @see NotifPipeline#getAllNotifs() */ /** @see NotifPipeline#getAllNotifs() */
Collection<NotificationEntry> getAllNotifs() { Collection<NotificationEntry> getAllNotifs() {
Assert.isMainThread(); Assert.isMainThread();

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection; package com.android.systemui.statusbar.notification.collection;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; 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. * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/ */
@Override
public Collection<NotificationEntry> getAllNotifs() { public Collection<NotificationEntry> getAllNotifs() {
return mNotifCollection.getAllNotifs(); return mNotifCollection.getAllNotifs();
} }
@@ -98,6 +101,14 @@ public class NotifPipeline implements CommonNotifCollection {
mNotifCollection.addCollectionListener(listener); 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 * 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. * dismissed or retracted by system server to be temporarily retained in the collection.

View File

@@ -23,9 +23,11 @@ import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.NotificationMediaManager 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.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager 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.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider 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
@@ -39,7 +41,6 @@ import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy import dagger.Lazy
import java.util.Objects import java.util.Objects
import javax.inject.Inject import javax.inject.Inject
import kotlin.Comparator
private const val TAG = "NotifRankingManager" private const val TAG = "NotifRankingManager"
@@ -60,10 +61,11 @@ open class NotificationRankingManager @Inject constructor(
private val logger: NotificationEntryManagerLogger, private val logger: NotificationEntryManagerLogger,
private val sectionsFeatureManager: NotificationSectionsFeatureManager, private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val peopleNotificationIdentifier: PeopleNotificationIdentifier, 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 protected set
private val mediaManager by lazy { private val mediaManager by lazy {
mediaManagerLazy.get() mediaManagerLazy.get()
@@ -115,7 +117,7 @@ open class NotificationRankingManager @Inject constructor(
} }
} }
fun updateRanking( override fun updateRanking(
newRankingMap: RankingMap?, newRankingMap: RankingMap?,
entries: Collection<NotificationEntry>, entries: Collection<NotificationEntry>,
reason: String 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 */ /** Uses the [rankingComparator] to sort notifications which aren't filtered */
private fun filterAndSortLocked( private fun filterAndSortLocked(
entries: Collection<NotificationEntry>, entries: Collection<NotificationEntry>,

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator; 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.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
@@ -27,5 +29,5 @@ public interface Coordinator {
* Called after the NewNotifPipeline is initialized. * Called after the NewNotifPipeline is initialized.
* Coordinators should register their listeners and {@link Pluggable}s to the pipeline. * Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
*/ */
void attach(NotifPipeline pipeline); void attach(@NonNull NotifPipeline pipeline);
} }

View File

@@ -60,6 +60,7 @@ public class NotifCoordinators implements Dumpable {
ConversationCoordinator conversationCoordinator, ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator, PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator, MediaCoordinator mediaCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
VisualStabilityCoordinator visualStabilityCoordinator) { VisualStabilityCoordinator visualStabilityCoordinator) {
dumpManager.registerDumpable(TAG, this); dumpManager.registerDumpable(TAG, this);
@@ -70,9 +71,14 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(appOpsCoordinator); mCoordinators.add(appOpsCoordinator);
mCoordinators.add(deviceProvisionedCoordinator); mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator); mCoordinators.add(bubbleCoordinator);
mCoordinators.add(mediaCoordinator);
mCoordinators.add(conversationCoordinator); mCoordinators.add(conversationCoordinator);
mCoordinators.add(mediaCoordinator);
mCoordinators.add(visualStabilityCoordinator); mCoordinators.add(visualStabilityCoordinator);
if (featureFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator);
}
if (featureFlags.isNewNotifPipelineRenderingEnabled()) { if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
mCoordinators.add(headsUpCoordinator); mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator); mCoordinators.add(preparationCoordinator);

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable; 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.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -45,5 +47,5 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
* various entries against. * various entries against.
* @return True if the notif should be removed from the list * @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);
} }

View File

@@ -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.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; 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.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
@@ -100,8 +99,6 @@ public interface NotificationsModule {
static NotificationEntryManager provideNotificationEntryManager( static NotificationEntryManager provideNotificationEntryManager(
NotificationEntryManagerLogger logger, NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
Lazy<NotificationRankingManager> rankingManager,
NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags, FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
@@ -111,8 +108,6 @@ public interface NotificationsModule {
return new NotificationEntryManager( return new NotificationEntryManager(
logger, logger,
groupManager, groupManager,
rankingManager,
keyguardEnvironment,
featureFlags, featureFlags,
notificationRowBinderLazy, notificationRowBinderLazy,
notificationRemoteInputManagerLazy, notificationRemoteInputManagerLazy,

View File

@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.init package com.android.systemui.statusbar.notification.init
import android.content.Context
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.people.widget.PeopleSpaceWidgetManager 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.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.NotifPipeline 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.TargetSdkResolver
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
@@ -59,10 +59,10 @@ import javax.inject.Inject
*/ */
@SysUISingleton @SysUISingleton
class NotificationsControllerImpl @Inject constructor( class NotificationsControllerImpl @Inject constructor(
private val context: Context,
private val featureFlags: FeatureFlags, private val featureFlags: FeatureFlags,
private val notificationListener: NotificationListener, private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager, private val entryManager: NotificationEntryManager,
private val legacyRanker: NotificationRankingManager,
private val notifPipeline: Lazy<NotifPipeline>, private val notifPipeline: Lazy<NotifPipeline>,
private val targetSdkResolver: TargetSdkResolver, private val targetSdkResolver: TargetSdkResolver,
private val newNotifPipeline: Lazy<NotifPipelineInitializer>, private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
@@ -128,6 +128,7 @@ class NotificationsControllerImpl @Inject constructor(
groupManagerLegacy.get().setHeadsUpManager(headsUpManager) groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
groupAlertTransferHelper.setHeadsUpManager(headsUpManager) groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
entryManager.setRanker(legacyRanker)
entryManager.attach(notificationListener) entryManager.attach(notificationListener)
} }

View File

@@ -16,9 +16,11 @@
package com.android.systemui.statusbar; package com.android.systemui.statusbar;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.content.Intent.ACTION_USER_SWITCHED; 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_MEDIA_CONTROLS;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT; 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.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController; 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.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -383,13 +386,44 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry)); 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 private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl { extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) { public TestNotificationLockscreenUserManager(Context context) {
super(context, mBroadcastDispatcher, mDevicePolicyManager, mUserManager, super(
mClickNotifier, NotificationLockscreenUserManagerTest.this.mKeyguardManager, context,
mStatusBarStateController, Handler.createAsync(Looper.myLooper()), mBroadcastDispatcher,
mDeviceProvisionedController, mKeyguardStateController); mDevicePolicyManager,
mUserManager,
mClickNotifier,
NotificationLockscreenUserManagerTest.this.mKeyguardManager,
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
mDeviceProvisionedController,
mKeyguardStateController);
} }
public BroadcastReceiver getBaseBroadcastReceiverForTest() { public BroadcastReceiver getBaseBroadcastReceiverForTest() {
@@ -404,4 +438,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
return mSettingsObserver; 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;
}
}
} }

View File

@@ -190,16 +190,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager = new NotificationEntryManager( mEntryManager = new NotificationEntryManager(
mLogger, mLogger,
mGroupManager, mGroupManager,
() -> new NotificationRankingManager(
() -> mNotificationMediaManager,
mGroupManager,
mHeadsUpManager,
mock(NotificationFilter.class),
mLogger,
mock(NotificationSectionsFeatureManager.class),
mock(PeopleNotificationIdentifier.class),
mock(HighPriorityProvider.class)),
mEnvironment,
mFeatureFlags, mFeatureFlags,
() -> mNotificationRowBinder, () -> mNotificationRowBinder,
() -> mRemoteInputManager, () -> mRemoteInputManager,
@@ -207,6 +197,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mock(ForegroundServiceDismissalFeatureController.class), mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.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.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener); mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor); mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);

View File

@@ -77,13 +77,16 @@ public class NotificationFilterTest extends SysuiTestCase {
mock(StatusBarNotification.class); mock(StatusBarNotification.class);
@Mock @Mock
ForegroundServiceController mFsc; StatusBarStateController mStatusBarStateController;
@Mock @Mock
KeyguardEnvironment mEnvironment; KeyguardEnvironment mEnvironment;
@Mock @Mock
MediaFeatureFlag mMediaFeatureFlag; ForegroundServiceController mFsc;
@Mock @Mock
StatusBarStateController mStatusBarStateController; NotificationLockscreenUserManager mUserManager;
@Mock
MediaFeatureFlag mMediaFeatureFlag;
private final IPackageManager mMockPackageManager = mock(IPackageManager.class); private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationFilter mNotificationFilter; private NotificationFilter mNotificationFilter;
@@ -127,7 +130,12 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency, mDependency,
TestableLooper.get(this)); TestableLooper.get(this));
mRow = testHelper.createRow(); mRow = testHelper.createRow();
mNotificationFilter = new NotificationFilter(mStatusBarStateController, mMediaFeatureFlag); mNotificationFilter = new NotificationFilter(
mStatusBarStateController,
mEnvironment,
mFsc,
mUserManager,
mMediaFeatureFlag);
} }
@After @After
@@ -195,7 +203,11 @@ public class NotificationFilterTest extends SysuiTestCase {
public void shouldFilterOtherNotificationWhenDisabled() { public void shouldFilterOtherNotificationWhenDisabled() {
// GIVEN that the media feature is disabled // GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false); when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
NotificationFilter filter = new NotificationFilter(mStatusBarStateController, NotificationFilter filter = new NotificationFilter(
mStatusBarStateController,
mEnvironment,
mFsc,
mUserManager,
mMediaFeatureFlag); mMediaFeatureFlag);
// WHEN the media filter is asked about an entry // WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build(); NotificationEntry otherEntry = new NotificationEntryBuilder().build();
@@ -208,7 +220,11 @@ public class NotificationFilterTest extends SysuiTestCase {
public void shouldFilterOtherNotificationWhenEnabled() { public void shouldFilterOtherNotificationWhenEnabled() {
// GIVEN that the media feature is enabled // GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true); when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
NotificationFilter filter = new NotificationFilter(mStatusBarStateController, NotificationFilter filter = new NotificationFilter(
mStatusBarStateController,
mEnvironment,
mFsc,
mUserManager,
mMediaFeatureFlag); mMediaFeatureFlag);
// WHEN the media filter is asked about an entry // WHEN the media filter is asked about an entry
NotificationEntry otherEntry = new NotificationEntryBuilder().build(); NotificationEntry otherEntry = new NotificationEntryBuilder().build();
@@ -221,7 +237,11 @@ public class NotificationFilterTest extends SysuiTestCase {
public void shouldFilterMediaNotificationWhenDisabled() { public void shouldFilterMediaNotificationWhenDisabled() {
// GIVEN that the media feature is disabled // GIVEN that the media feature is disabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(false); when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
NotificationFilter filter = new NotificationFilter(mStatusBarStateController, NotificationFilter filter = new NotificationFilter(
mStatusBarStateController,
mEnvironment,
mFsc,
mUserManager,
mMediaFeatureFlag); mMediaFeatureFlag);
// WHEN the media filter is asked about a media entry // WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry); final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
@@ -233,7 +253,11 @@ public class NotificationFilterTest extends SysuiTestCase {
public void shouldFilterMediaNotificationWhenEnabled() { public void shouldFilterMediaNotificationWhenEnabled() {
// GIVEN that the media feature is enabled // GIVEN that the media feature is enabled
when(mMediaFeatureFlag.getEnabled()).thenReturn(true); when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
NotificationFilter filter = new NotificationFilter(mStatusBarStateController, NotificationFilter filter = new NotificationFilter(
mStatusBarStateController,
mEnvironment,
mFsc,
mUserManager,
mMediaFeatureFlag); mMediaFeatureFlag);
// WHEN the media filter is asked about a media entry // WHEN the media filter is asked about a media entry
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry); final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);

View File

@@ -30,9 +30,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
import com.android.systemui.statusbar.NotificationMediaManager 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.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager 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.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON 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_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE 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.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpManager
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import dagger.Lazy import dagger.Lazy
@@ -79,9 +80,11 @@ class NotificationRankingManagerTest : SysuiTestCase() {
mock(NotificationEntryManagerLogger::class.java), mock(NotificationEntryManagerLogger::class.java),
sectionsManager, sectionsManager,
personNotificationIdentifier, personNotificationIdentifier,
HighPriorityProvider(personNotificationIdentifier, HighPriorityProvider(
mock(NotificationGroupManagerLegacy::class.java)) personNotificationIdentifier,
) mock(NotificationGroupManagerLegacy::class.java)),
mock(KeyguardEnvironment::class.java)
)
} }
@Test @Test
@@ -486,7 +489,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
logger: NotificationEntryManagerLogger, logger: NotificationEntryManagerLogger,
sectionsFeatureManager: NotificationSectionsFeatureManager, sectionsFeatureManager: NotificationSectionsFeatureManager,
peopleNotificationIdentifier: PeopleNotificationIdentifier, peopleNotificationIdentifier: PeopleNotificationIdentifier,
highPriorityProvider: HighPriorityProvider highPriorityProvider: HighPriorityProvider,
keyguardEnvironment: KeyguardEnvironment
) : NotificationRankingManager( ) : NotificationRankingManager(
mediaManager, mediaManager,
groupManager, groupManager,
@@ -495,7 +499,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
logger, logger,
sectionsFeatureManager, sectionsFeatureManager,
peopleNotificationIdentifier, peopleNotificationIdentifier,
highPriorityProvider highPriorityProvider,
keyguardEnvironment
) { ) {
fun applyTestRankingMap(r: RankingMap) { fun applyTestRankingMap(r: RankingMap) {
rankingMap = r rankingMap = r

View File

@@ -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"

View File

@@ -181,16 +181,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mEntryManager = new NotificationEntryManager( mEntryManager = new NotificationEntryManager(
mock(NotificationEntryManagerLogger.class), mock(NotificationEntryManagerLogger.class),
mGroupMembershipManager, 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, mFeatureFlags,
() -> mRowBinder, () -> mRowBinder,
() -> mRemoteInputManager, () -> mRemoteInputManager,
@@ -198,6 +188,17 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mock(ForegroundServiceDismissalFeatureController.class), mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.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); NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
NotifBindPipeline pipeline = new NotifBindPipeline( NotifBindPipeline pipeline = new NotifBindPipeline(