New pipeline: Fix notification re-inflation on dark mode change.

Fixes: 201329618
Fixes: 201413798
Test: tap dark mode QS tile
Test: atest PreparationCoordinatorTest ShadeEventCoordinatorTest
Change-Id: I05931f89596b4145f2c5d31cb32b2d451fc280b6
This commit is contained in:
Jeff DeCew
2021-09-28 16:27:26 +00:00
parent d5b52e01d1
commit fc40850f0e
15 changed files with 548 additions and 46 deletions

View File

@@ -15,7 +15,7 @@ class GutsCoordinatorLogger @Inject constructor(
fun logGutsOpened(key: String, guts: NotificationGuts) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
str2 = guts::class.simpleName
str2 = guts.gutsContent::class.simpleName
bool1 = guts.isLeavebehind
}, {
"Guts of type $str2 (leave behind: $bool1) opened for class $str1"

View File

@@ -61,7 +61,9 @@ public class NotifCoordinators implements Dumpable {
ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
ShadeEventCoordinator shadeEventCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
ViewConfigCoordinator viewConfigCoordinator,
VisualStabilityCoordinator visualStabilityCoordinator,
CommunalCoordinator communalCoordinator) {
dumpManager.registerDumpable(TAG, this);
@@ -75,6 +77,8 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(bubbleCoordinator);
mCoordinators.add(conversationCoordinator);
mCoordinators.add(mediaCoordinator);
mCoordinators.add(shadeEventCoordinator);
mCoordinators.add(viewConfigCoordinator);
mCoordinators.add(visualStabilityCoordinator);
mCoordinators.add(communalCoordinator);

View File

@@ -0,0 +1,78 @@
/*
* 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.service.notification.NotificationListenerService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource
import javax.inject.Inject
/**
* A coordinator which provides callbacks to a view surfaces for various events relevant to the
* shade, such as when the user removes a notification, or when the shade is emptied.
*/
@SysUISingleton
class ShadeEventCoordinator @Inject internal constructor(
private val mLogger: ShadeEventCoordinatorLogger
) : Coordinator, NotifShadeEventSource {
private var mNotifRemovedByUserCallback: Runnable? = null
private var mShadeEmptiedCallback: Runnable? = null
private var mEntryRemoved = false
private var mEntryRemovedByUser = false
override fun attach(pipeline: NotifPipeline) {
pipeline.addCollectionListener(mNotifCollectionListener)
pipeline.addOnBeforeRenderListListener(this::onBeforeRenderList)
}
private val mNotifCollectionListener = object : NotifCollectionListener {
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
mEntryRemoved = true
mEntryRemovedByUser =
reason == NotificationListenerService.REASON_CLICK ||
reason == NotificationListenerService.REASON_CANCEL_ALL ||
reason == NotificationListenerService.REASON_CANCEL
}
}
override fun setNotifRemovedByUserCallback(callback: Runnable) {
check(mNotifRemovedByUserCallback == null) { "mNotifRemovedByUserCallback already set" }
mNotifRemovedByUserCallback = callback
}
override fun setShadeEmptiedCallback(callback: Runnable) {
check(mShadeEmptiedCallback == null) { "mShadeEmptiedCallback already set" }
mShadeEmptiedCallback = callback
}
private fun onBeforeRenderList(entries: List<ListEntry>) {
if (mEntryRemoved && entries.isEmpty()) {
mLogger.logShadeEmptied()
mShadeEmptiedCallback?.run()
}
if (mEntryRemoved && mEntryRemovedByUser) {
mLogger.logNotifRemovedByUser()
mNotifRemovedByUserCallback?.run()
}
mEntryRemoved = false
mEntryRemovedByUser = false
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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 com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationLog
import javax.inject.Inject
private const val TAG = "ShadeEventCoordinator"
/** Logger for the [ShadeEventCoordinator] */
class ShadeEventCoordinatorLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
fun logShadeEmptied() {
buffer.log(TAG, LogLevel.DEBUG, { }, { "Shade emptied" })
}
fun logNotifRemovedByUser() {
buffer.log(TAG, LogLevel.DEBUG, { }, { "Notification removed by user" })
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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 com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
/**
* A coordinator which ensures that notifications within the new pipeline are correctly inflated
* for the current uiMode and screen properties; additionally deferring those changes when a user
* change is in progress until that process has completed.
*/
@SysUISingleton
class ViewConfigCoordinator @Inject internal constructor(
configurationController: ConfigurationController,
lockscreenUserManager: NotificationLockscreenUserManagerImpl,
featureFlags: FeatureFlags,
private val mGutsManager: NotificationGutsManager,
private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener {
private var mReinflateNotificationsOnUserSwitched = false
private var mDispatchUiModeChangeOnUserSwitched = false
private var mPipeline: NotifPipeline? = null
init {
if (featureFlags.isNewNotifPipelineRenderingEnabled) {
lockscreenUserManager.addUserChangedListener(this)
configurationController.addCallback(this)
}
}
override fun attach(pipeline: NotifPipeline) {
mPipeline = pipeline
}
override fun onDensityOrFontScaleChanged() {
MessagingMessage.dropCache()
MessagingGroup.dropCache()
if (!mKeyguardUpdateMonitor.isSwitchingUser) {
updateNotificationsOnDensityOrFontScaleChanged()
} else {
mReinflateNotificationsOnUserSwitched = true
}
}
override fun onUiModeChanged() {
if (!mKeyguardUpdateMonitor.isSwitchingUser) {
updateNotificationsOnUiModeChanged()
} else {
mDispatchUiModeChangeOnUserSwitched = true
}
}
override fun onThemeChanged() {
onDensityOrFontScaleChanged()
}
override fun onUserChanged(userId: Int) {
if (mReinflateNotificationsOnUserSwitched) {
updateNotificationsOnDensityOrFontScaleChanged()
mReinflateNotificationsOnUserSwitched = false
}
if (mDispatchUiModeChangeOnUserSwitched) {
updateNotificationsOnUiModeChanged()
mDispatchUiModeChangeOnUserSwitched = false
}
}
private fun updateNotificationsOnUiModeChanged() {
mPipeline?.allNotifs?.forEach { entry ->
val row = entry.row
row?.onUiModeChanged()
}
}
private fun updateNotificationsOnDensityOrFontScaleChanged() {
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
val exposedGuts = entry.areGutsExposed()
if (exposedGuts) {
mGutsManager.onDensityOrFontScaleChanged(entry)
}
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.legacy;
import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import org.jetbrains.annotations.NotNull;
import javax.inject.Inject;
/**
* This is some logic extracted from the
* {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter}
* into a class that implements a new-pipeline interface so that the new pipeline can implement it
* correctly.
*
* Specifically, this is the logic which updates notifications when uiMode and screen properties
* change, and which closes the shade when the last notification disappears.
*/
public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource {
private static final String TAG = "LegacyNotifPresenter";
private final NotificationEntryManager mEntryManager;
private boolean mEntryListenerAdded;
private Runnable mShadeEmptiedCallback;
private Runnable mNotifRemovedByUserCallback;
@Inject
public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) {
mEntryManager = entryManager;
}
private void ensureEntryListenerAdded() {
if (mEntryListenerAdded) return;
mEntryListenerAdded = true;
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onEntryRemoved(
@NotNull NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
StatusBarNotification old = entry.getSbn();
if (SPEW) {
Log.d(TAG, "removeNotification key=" + entry.getKey()
+ " old=" + old + " reason=" + reason);
}
if (old != null && !mEntryManager.hasActiveNotifications()) {
if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run();
}
if (removedByUser) {
if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run();
}
}
});
}
@Override
public void setNotifRemovedByUserCallback(@NonNull Runnable callback) {
if (mNotifRemovedByUserCallback != null) {
throw new IllegalStateException("mNotifRemovedByUserCallback already set");
}
mNotifRemovedByUserCallback = callback;
ensureEntryListenerAdded();
}
@Override
public void setShadeEmptiedCallback(@NonNull Runnable callback) {
if (mShadeEmptiedCallback != null) {
throw new IllegalStateException("mShadeEmptiedCallback already set");
}
mShadeEmptiedCallback = callback;
ensureEntryListenerAdded();
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.render
/**
* This is an object which provides callbacks for certain important events related to the
* notification shade, such as notifications being removed by the user, or the shade becoming empty.
*/
interface NotifShadeEventSource {
/**
* Registers a callback to be invoked when the last notification has been removed from
* the shade for any reason
*/
fun setShadeEmptiedCallback(callback: Runnable)
/**
* Registers a callback to be invoked when a notification has been removed from
* the shade by a user action
*/
fun setNotifRemovedByUserCallback(callback: Runnable)
}

View File

@@ -45,10 +45,12 @@ 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.coordinator.ShadeEventCoordinator;
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;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -59,6 +61,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -90,7 +93,7 @@ import dagger.Provides;
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
@Module(includes = { NotificationSectionHeadersModule.class })
@Module(includes = {NotificationSectionHeadersModule.class})
public interface NotificationsModule {
@Binds
StackScrollAlgorithm.SectionProvider bindSectionProvider(
@@ -270,6 +273,20 @@ public interface NotificationsModule {
return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
}
/**
* Provide the active implementation for presenting notifications.
*/
@Provides
@SysUISingleton
static NotifShadeEventSource provideNotifShadeEventSource(
FeatureFlags featureFlags,
Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy,
Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) {
return featureFlags.isNewNotifPipelineRenderingEnabled()
? shadeEventCoordinatorLazy.get()
: legacyNotificationPresenterExtensionsLazy.get();
}
/**
* Provide a dismissal callback that's triggered when a user manually dismissed a notification
* from the notification shade or it gets auto-cancelled by click.

View File

@@ -1249,6 +1249,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onConfigurationChanged();
}

View File

@@ -207,6 +207,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -535,6 +536,7 @@ public class StatusBar extends SystemUI implements
private final int[] mAbsPos = new int[2];
private final NotifShadeEventSource mNotifShadeEventSource;
protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
@@ -718,6 +720,7 @@ public class StatusBar extends SystemUI implements
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -823,6 +826,7 @@ public class StatusBar extends SystemUI implements
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
@@ -1506,6 +1510,7 @@ public class StatusBar extends SystemUI implements
mDynamicPrivacyController,
mKeyguardStateController,
mKeyguardIndicationController,
mFeatureFlags,
this /* statusBar */,
mShadeController,
mLockscreenShadeTransitionController,
@@ -1513,6 +1518,7 @@ public class StatusBar extends SystemUI implements
mViewHierarchyManager,
mLockscreenUserManager,
mStatusBarStateController,
mNotifShadeEventSource,
mEntryManager,
mMediaManager,
mGutsManager,

View File

@@ -17,9 +17,7 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.RemoteException;
@@ -36,7 +34,6 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -44,6 +41,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.CommandQueue;
@@ -59,10 +57,10 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -88,6 +86,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SysuiStatusBarStateController mStatusBarStateController;
private final NotifShadeEventSource mNotifShadeEventSource;
private final NotificationEntryManager mEntryManager;
private final NotificationMediaManager mMediaManager;
private final NotificationGutsManager mGutsManager;
@@ -100,6 +99,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final KeyguardIndicationController mKeyguardIndicationController;
private final FeatureFlags mFeatureFlags;
private final StatusBar mStatusBar;
private final ShadeController mShadeController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -127,6 +127,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
FeatureFlags featureFlags,
StatusBar statusBar,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
@@ -134,6 +135,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationViewHierarchyManager notificationViewHierarchyManager,
NotificationLockscreenUserManager lockscreenUserManager,
SysuiStatusBarStateController sysuiStatusBarStateController,
NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationMediaManager notificationMediaManager,
NotificationGutsManager notificationGutsManager,
@@ -148,6 +150,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
mKeyguardIndicationController = keyguardIndicationController;
mFeatureFlags = featureFlags;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
mShadeController = shadeController;
@@ -156,6 +159,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mViewHierarchyManager = notificationViewHierarchyManager;
mLockscreenUserManager = lockscreenUserManager;
mStatusBarStateController = sysuiStatusBarStateController;
mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mMediaManager = notificationMediaManager;
mGutsManager = notificationGutsManager;
@@ -186,30 +190,18 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mNotificationPanel.createRemoteInputDelegate());
initController.addPostInitTask(() -> {
NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
@Override
public void onEntryRemoved(
@Nullable NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
StatusBarNotificationPresenter.this.onNotificationRemoved(
entry.getKey(), entry.getSbn(), reason);
if (removedByUser) {
maybeEndAmbientPulse();
}
}
};
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotificationListContainer());
mEntryManager.setUpWithPresenter(this);
mEntryManager.addNotificationEntryListener(notificationEntryListener);
mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mEntryManager.setUpWithPresenter(this);
mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
}
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
@@ -226,8 +218,21 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
configurationController.addCallback(this);
}
/** Called when the shade has been emptied to attempt to close the shade */
private void maybeClosePanelForShadeEmptied() {
if (CLOSE_PANEL_WHEN_EMPTIED
&& !mNotificationPanel.isTracking()
&& !mNotificationPanel.isQsExpanded()
&& mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
&& !isCollapsing()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
}
@Override
public void onDensityOrFontScaleChanged() {
// TODO(b/145659174): Remove legacy pipeline code
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
MessagingMessage.dropCache();
MessagingGroup.dropCache();
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
@@ -239,8 +244,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
@Override
public void onUiModeChanged() {
// TODO(b/145659174): Remove legacy pipeline code
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
updateNotificationOnUiModeChanged();
updateNotificationsOnUiModeChanged();
} else {
mDispatchUiModeChangeOnUserSwitched = true;
}
@@ -251,7 +258,9 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
onDensityOrFontScaleChanged();
}
private void updateNotificationOnUiModeChanged() {
private void updateNotificationsOnUiModeChanged() {
// TODO(b/145659174): Remove legacy pipeline code
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -264,6 +273,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
private void updateNotificationsOnDensityOrFontScaleChanged() {
// TODO(b/145659174): Remove legacy pipeline code
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -276,6 +287,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
}
@Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
@@ -308,21 +320,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mNotificationPanel.updateNotificationViews(reason);
}
private void onNotificationRemoved(String key, StatusBarNotification old, int reason) {
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
&& !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()
&& mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
&& !isCollapsing()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
}
public boolean hasActiveNotifications() {
return mEntryManager.hasActiveNotifications();
}
@Override
public void onUserSwitched(int newUserId) {
// Begin old BaseStatusBar.userSwitched
@@ -335,7 +332,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mReinflateNotificationsOnUserSwitched = false;
}
if (mDispatchUiModeChangeOnUserSwitched) {
updateNotificationOnUiModeChanged();
updateNotificationsOnUiModeChanged();
mDispatchUiModeChangeOnUserSwitched = false;
}
updateNotificationViews("user switched");

View File

@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -157,6 +158,7 @@ public interface StatusBarPhoneModule {
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -260,6 +262,7 @@ public interface StatusBarPhoneModule {
falsingManager,
falsingCollector,
broadcastDispatcher,
notifShadeEventSource,
notificationEntryManager,
notificationGutsManager,
notificationLogger,

View File

@@ -0,0 +1,106 @@
/*
* 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.service.notification.NotificationListenerService.REASON_APP_CANCEL
import android.service.notification.NotificationListenerService.REASON_CANCEL
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.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.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.util.mockito.argumentCaptor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class ShadeEventCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: ShadeEventCoordinator
private lateinit var notifCollectionListener: NotifCollectionListener
private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
private lateinit var entry1: NotificationEntry
private lateinit var entry2: NotificationEntry
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var logger: ShadeEventCoordinatorLogger
@Mock private lateinit var notifRemovedByUserCallback: Runnable
@Mock private lateinit var shadeEmptiedCallback: Runnable
@Before
fun setUp() {
initMocks(this)
coordinator = ShadeEventCoordinator(logger)
coordinator.attach(pipeline)
notifCollectionListener = argumentCaptor<NotifCollectionListener>().let {
verify(pipeline).addCollectionListener(it.capture())
it.value!!
}
onBeforeRenderListListener = argumentCaptor<OnBeforeRenderListListener>().let {
verify(pipeline).addOnBeforeRenderListListener(it.capture())
it.value!!
}
coordinator.setNotifRemovedByUserCallback(notifRemovedByUserCallback)
coordinator.setShadeEmptiedCallback(shadeEmptiedCallback)
entry1 = NotificationEntryBuilder().setId(1).build()
entry2 = NotificationEntryBuilder().setId(2).build()
}
@Test
fun testUserCancelLastNotification() {
notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
verify(shadeEmptiedCallback, never()).run()
verify(notifRemovedByUserCallback, never()).run()
onBeforeRenderListListener.onBeforeRenderList(listOf())
verify(shadeEmptiedCallback).run()
verify(notifRemovedByUserCallback).run()
}
@Test
fun testAppCancelLastNotification() {
notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
onBeforeRenderListListener.onBeforeRenderList(listOf())
verify(shadeEmptiedCallback).run()
verify(notifRemovedByUserCallback, never()).run()
}
@Test
fun testUserCancelOneOfTwoNotifications() {
notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
verify(shadeEmptiedCallback, never()).run()
verify(notifRemovedByUserCallback).run()
}
@Test
fun testAppCancelOneOfTwoNotifications() {
notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
verify(shadeEmptiedCallback, never()).run()
verify(notifRemovedByUserCallback, never()).run()
}
}

View File

@@ -37,6 +37,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -51,6 +52,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -109,12 +111,15 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(DozeScrimController.class), mock(ScrimController.class),
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
mock(KeyguardIndicationController.class),
mock(FeatureFlags.class),
mStatusBar,
mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
mCommandQueue,
mock(NotificationViewHierarchyManager.class),
mock(NotificationLockscreenUserManager.class),
mock(SysuiStatusBarStateController.class),
mock(NotifShadeEventSource.class),
mock(NotificationEntryManager.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),

View File

@@ -121,6 +121,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -210,6 +211,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
@Mock private NotifShadeEventSource mNotifShadeEventSource;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@@ -377,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase {
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
mNotifShadeEventSource,
mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,