diff options
6 files changed, 104 insertions, 4 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 3002a6820990..a2379b270f49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisibilityLocationProviderDelegator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisibilityLocationProviderDelegator.kt new file mode 100644 index 000000000000..4bc4ecf5dae9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisibilityLocationProviderDelegator.kt @@ -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) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index df2de560b5b9..a7b7a239f4c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index e13378269ba6..0240bbcf1e19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -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) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index b4a5f5ce205f..e488f39bf27d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -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); @@ -355,6 +360,38 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { } @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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 90061b078afe..026c82eda42d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -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, |