Merge "Ensure visible notifications do not reorder when they HUN." into tm-qpr-dev

This commit is contained in:
Jeff DeCew
2022-11-16 14:32:47 +00:00
committed by Android (Google) Code Review
6 changed files with 104 additions and 4 deletions

View File

@@ -29,6 +29,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeStateEvents;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -62,6 +63,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
private final HeadsUpManager mHeadsUpManager;
private final ShadeStateEvents mShadeStateEvents;
private final StatusBarStateController mStatusBarStateController;
private final VisibilityLocationProvider mVisibilityLocationProvider;
private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -94,9 +96,11 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
HeadsUpManager headsUpManager,
ShadeStateEvents shadeStateEvents,
StatusBarStateController statusBarStateController,
VisibilityLocationProvider visibilityLocationProvider,
VisualStabilityProvider visualStabilityProvider,
WakefulnessLifecycle wakefulnessLifecycle) {
mHeadsUpManager = headsUpManager;
mVisibilityLocationProvider = visibilityLocationProvider;
mVisualStabilityProvider = visualStabilityProvider;
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
@@ -123,6 +127,11 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
// HUNs to the top of the shade
private final NotifStabilityManager mNotifStabilityManager =
new NotifStabilityManager("VisualStabilityCoordinator") {
private boolean canMoveForHeadsUp(NotificationEntry entry) {
return entry != null && mHeadsUpManager.isAlerting(entry.getKey())
&& !mVisibilityLocationProvider.isInVisibleLocation(entry);
}
@Override
public void onBeginRun() {
mIsSuppressingPipelineRun = false;
@@ -140,7 +149,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
@Override
public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isGroupChangeAllowedForEntry =
mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
mReorderingAllowed || canMoveForHeadsUp(entry);
mIsSuppressingGroupChange |= !isGroupChangeAllowedForEntry;
return isGroupChangeAllowedForEntry;
}
@@ -156,7 +165,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isSectionChangeAllowedForEntry =
mReorderingAllowed
|| mHeadsUpManager.isAlerting(entry.getKey())
|| canMoveForHeadsUp(entry)
|| mEntriesThatCanChangeSection.containsKey(entry.getKey());
if (!isSectionChangeAllowedForEntry) {
mEntriesWithSuppressedSectionChange.add(entry.getKey());
@@ -165,8 +174,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
}
@Override
public boolean isEntryReorderingAllowed(@NonNull ListEntry section) {
return mReorderingAllowed;
public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
return mReorderingAllowed || canMoveForHeadsUp(entry.getRepresentativeEntry());
}
@Override

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.provider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.VisibilityLocationProvider
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
/**
* An injectable component which delegates the visibility location computation to a delegate which
* can be initialized after the initial injection, generally because it's provided by a view.
*/
@SysUISingleton
class VisibilityLocationProviderDelegator @Inject constructor() : VisibilityLocationProvider {
private var delegate: VisibilityLocationProvider? = null
fun setDelegate(provider: VisibilityLocationProvider) {
delegate = provider
}
override fun isInVisibleLocation(entry: NotificationEntry): Boolean =
requireNotNull(this.delegate) { "delegate not initialized" }.isInVisibleLocation(entry)
}

View File

@@ -37,6 +37,7 @@ import com.android.systemui.shade.ShadeEventsModule;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
@@ -51,6 +52,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.OnUserIn
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -151,6 +153,11 @@ public interface NotificationsModule {
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
/** Provides an instance of {@link VisibilityLocationProvider} */
@Binds
VisibilityLocationProvider bindVisibilityLocationProvider(
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator);
/** Provides an instance of {@link NotificationLogger} */
@SysUISingleton
@Provides

View File

@@ -89,6 +89,7 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
@@ -157,6 +158,7 @@ public class NotificationStackScrollLayoutController {
private final NotifCollection mNotifCollection;
private final UiEventLogger mUiEventLogger;
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
private final ShadeController mShadeController;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -638,6 +640,7 @@ public class NotificationStackScrollLayoutController {
ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
ShadeController shadeController,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
@@ -679,6 +682,7 @@ public class NotificationStackScrollLayoutController {
mNotifCollection = notifCollection;
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
mShadeController = shadeController;
mFeatureFlags = featureFlags;
mNotificationTargetsHelper = notificationTargetsHelper;
@@ -750,6 +754,8 @@ public class NotificationStackScrollLayoutController {
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation);
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -38,6 +40,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeStateEvents;
import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -73,6 +76,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private ShadeStateEvents mShadeStateEvents;
@Mock private VisibilityLocationProvider mVisibilityLocationProvider;
@Mock private VisualStabilityProvider mVisualStabilityProvider;
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@@ -100,6 +104,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mHeadsUpManager,
mShadeStateEvents,
mStatusBarStateController,
mVisibilityLocationProvider,
mVisualStabilityProvider,
mWakefulnessLifecycle);
@@ -354,6 +359,38 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
verifyStabilityManagerWasInvalidated(times(1));
}
@Test
public void testMovingVisibleHeadsUpNotAllowed() {
// GIVEN stability enforcing conditions
setPanelExpanded(true);
setSleepy(false);
// WHEN a notification is alerting and visible
when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(true);
when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
.thenReturn(true);
// VERIFY the notification cannot be reordered
assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isFalse();
assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isFalse();
}
@Test
public void testMovingInvisibleHeadsUpAllowed() {
// GIVEN stability enforcing conditions
setPanelExpanded(true);
setSleepy(false);
// WHEN a notification is alerting but not visible
when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(true);
when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
.thenReturn(false);
// VERIFY the notification can be reordered
assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isTrue();
assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isTrue();
}
@Test
public void testNeverSuppressedChanges_noInvalidationCalled() {
// GIVEN no notifications are currently being suppressed from grouping nor being sorted

View File

@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
@@ -122,6 +123,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private StackStateLogger mStackLogger;
@@ -173,6 +175,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mShadeTransitionController,
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,
mShadeController,
mJankMonitor,
mStackLogger,