Merge "Change HeadsUpManagerPhone to use VisualStabilityProvider"

This commit is contained in:
Jeff DeCew
2022-02-08 20:32:57 +00:00
committed by Android (Google) Code Review
8 changed files with 217 additions and 28 deletions

View File

@@ -49,6 +49,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -174,6 +175,7 @@ public abstract class SystemUIDefaultModule {
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
GroupMembershipManager groupManager,
VisualStabilityProvider visualStabilityProvider,
ConfigurationController configurationController) {
return new HeadsUpManagerPhone(
context,
@@ -181,6 +183,7 @@ public abstract class SystemUIDefaultModule {
statusBarStateController,
bypassController,
groupManager,
visualStabilityProvider,
configurationController
);
}

View File

@@ -1,25 +1,57 @@
package com.android.systemui.statusbar.notification.collection.provider
import android.util.ArraySet
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.ListenerSet
import javax.inject.Inject
@SysUISingleton
class VisualStabilityProvider @Inject constructor() {
private val listeners = ListenerSet<OnReorderingAllowedListener>()
/** All persistent and temporary listeners, in the order they were added */
private val allListeners = ListenerSet<OnReorderingAllowedListener>()
/** The subset of active listeners which are temporary (will be removed after called) */
private val temporaryListeners = ArraySet<OnReorderingAllowedListener>()
var isReorderingAllowed = true
set(value) {
if (field != value) {
field = value
if (value) {
listeners.forEach(OnReorderingAllowedListener::onReorderingAllowed)
notifyReorderingAllowed()
}
}
}
fun addPersistentReorderingAllowedListener(listener: OnReorderingAllowedListener) =
listeners.addIfAbsent(listener)
private fun notifyReorderingAllowed() {
allListeners.forEach { listener ->
if (temporaryListeners.remove(listener)) {
allListeners.remove(listener)
}
listener.onReorderingAllowed()
}
}
/** Add a listener which will be called until it is explicitly removed. */
fun addPersistentReorderingAllowedListener(listener: OnReorderingAllowedListener) {
temporaryListeners.remove(listener)
allListeners.addIfAbsent(listener)
}
/** Add a listener which will be removed when it is called. */
fun addTemporaryReorderingAllowedListener(listener: OnReorderingAllowedListener) {
// Only add to the temporary set if it was added to the global set
// to keep permanent listeners permanent
if (allListeners.addIfAbsent(listener)) {
temporaryListeners.add(listener)
}
}
/** Remove a listener from receiving any callbacks, whether it is persistent or temporary. */
fun removeReorderingAllowedListener(listener: OnReorderingAllowedListener) {
temporaryListeners.remove(listener)
allListeners.remove(listener)
}
}
fun interface OnReorderingAllowedListener {

View File

@@ -33,7 +33,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -52,7 +53,7 @@ import java.util.Stack;
* A implementation of HeadsUpManager for phone and car.
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
VisualStabilityManager.Callback, OnHeadsUpChangedListener {
OnHeadsUpChangedListener {
private static final String TAG = "HeadsUpManagerPhone";
@VisibleForTesting
@@ -60,8 +61,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
private final KeyguardBypassController mBypassController;
private final GroupMembershipManager mGroupMembershipManager;
private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
// TODO (b/162832756): remove visual stability manager when migrating to new pipeline
private VisualStabilityManager mVisualStabilityManager;
private final VisualStabilityProvider mVisualStabilityProvider;
private boolean mReleaseOnExpandFinish;
private boolean mTrackingHeadsUp;
@@ -104,6 +104,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
GroupMembershipManager groupMembershipManager,
VisualStabilityProvider visualStabilityProvider,
ConfigurationController configurationController) {
super(context, logger);
Resources resources = mContext.getResources();
@@ -111,6 +112,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
statusBarStateController.addCallback(mStatusBarStateListener);
mBypassController = bypassController;
mGroupMembershipManager = groupMembershipManager;
mVisualStabilityProvider = visualStabilityProvider;
updateResources();
configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@@ -126,10 +128,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
});
}
void setup(VisualStabilityManager visualStabilityManager) {
mVisualStabilityManager = visualStabilityManager;
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
mAnimationStateHandler = handler;
}
@@ -333,14 +331,13 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
// We should not defer the removal if reordering isn't allowed since otherwise
// these won't disappear until reordering is allowed again, which happens only once
// the notification panel is collapsed again.
return mVisualStabilityManager.isReorderingAllowed() && super.shouldExtendLifetime(entry);
return mVisualStabilityProvider.isReorderingAllowed() && super.shouldExtendLifetime(entry);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// VisualStabilityManager.Callback overrides:
// OnReorderingAllowedListener:
@Override
public void onChangeAllowed() {
private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
if (isAlerting(entry.getKey())) {
@@ -350,7 +347,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
mEntriesToRemoveWhenReorderingAllowed.clear();
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpManager utility (protected) methods overrides:
@@ -431,13 +428,13 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
public void setEntry(@NonNull final NotificationEntry entry) {
Runnable removeHeadsUpRunnable = () -> {
if (!mVisualStabilityManager.isReorderingAllowed()
if (!mVisualStabilityProvider.isReorderingAllowed()
// We don't want to allow reordering while pulsing, but headsup need to
// time out anyway
&& !entry.showingPulsing()) {
mEntriesToRemoveWhenReorderingAllowed.add(entry);
mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManagerPhone.this,
false /* persistent */);
mVisualStabilityProvider.addTemporaryReorderingAllowedListener(
mOnReorderingAllowedListener);
} else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
} else {

View File

@@ -1185,10 +1185,11 @@ public class StatusBar extends CoreStartable implements
});
initializer.initializeStatusBar(mStatusBarComponent);
mHeadsUpManager.setup(mVisualStabilityManager);
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
mHeadsUpManager.addListener(mVisualStabilityManager);
}
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);

View File

@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -167,6 +168,7 @@ public abstract class TvSystemUIModule {
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
GroupMembershipManager groupManager,
VisualStabilityProvider visualStabilityProvider,
ConfigurationController configurationController) {
return new HeadsUpManagerPhone(
context,
@@ -174,6 +176,7 @@ public abstract class TvSystemUIModule {
statusBarStateController,
bypassController,
groupManager,
visualStabilityProvider,
configurationController
);
}

View File

@@ -0,0 +1,151 @@
/*
* 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 android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
@SmallTest
@RunWith(AndroidTestingRunner::class)
class VisualStabilityProviderTest : SysuiTestCase() {
private val visualStabilityProvider = VisualStabilityProvider()
private val listener: OnReorderingAllowedListener = mock()
@After
fun tearDown() {
// Verify that every interaction is verified in every test
verifyNoMoreInteractions(listener)
}
@Test
fun testPersistentListenerIgnoredIfStateNotChanged() {
visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = true
verify(listener, never()).onReorderingAllowed()
}
@Test
fun testPersistentListenerCalledTwice() {
visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(2)).onReorderingAllowed()
}
@Test
fun testTemporaryListenerCalledOnce() {
visualStabilityProvider.addTemporaryReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(1)).onReorderingAllowed()
}
@Test
fun testPersistentListenerCanBeRemoved() {
visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
visualStabilityProvider.removeReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, never()).onReorderingAllowed()
}
@Test
fun testTemporaryListenerCanBeRemoved() {
visualStabilityProvider.addTemporaryReorderingAllowedListener(listener)
visualStabilityProvider.removeReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, never()).onReorderingAllowed()
}
@Test
fun testPersistentListenerStaysPersistent() {
visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
visualStabilityProvider.addTemporaryReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(2)).onReorderingAllowed()
}
@Test
fun testTemporaryListenerBecomesPersistent() {
visualStabilityProvider.addTemporaryReorderingAllowedListener(listener)
visualStabilityProvider.addPersistentReorderingAllowedListener(listener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(listener, times(2)).onReorderingAllowed()
}
@Test
fun testPersistentListenerCanRemoveSelf() {
val selfRemovingListener = spy(object : OnReorderingAllowedListener {
override fun onReorderingAllowed() {
visualStabilityProvider.removeReorderingAllowedListener(this)
}
})
visualStabilityProvider.addPersistentReorderingAllowedListener(selfRemovingListener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(selfRemovingListener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(selfRemovingListener, times(1)).onReorderingAllowed()
}
@Test
fun testTemporaryListenerCanReAddSelf() {
val selfAddingListener = spy(object : OnReorderingAllowedListener {
override fun onReorderingAllowed() {
visualStabilityProvider.addTemporaryReorderingAllowedListener(this)
}
})
visualStabilityProvider.addTemporaryReorderingAllowedListener(selfAddingListener)
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(selfAddingListener, times(1)).onReorderingAllowed()
visualStabilityProvider.isReorderingAllowed = false
visualStabilityProvider.isReorderingAllowed = true
verify(selfAddingListener, times(2)).onReorderingAllowed()
}
}

View File

@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -139,6 +140,7 @@ public class NotificationTestHelper {
mStatusBarStateController,
mock(KeyguardBypassController.class),
mock(NotificationGroupManagerLegacy.class),
mock(VisualStabilityProvider.class),
mock(ConfigurationControllerImpl.class)
);
mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);

View File

@@ -36,7 +36,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
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.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
@@ -62,7 +62,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
@Mock private HeadsUpManagerLogger mHeadsUpManagerLogger;
@Mock private NotificationGroupManagerLegacy mGroupManager;
@Mock private View mNotificationShadeWindowView;
@Mock private VisualStabilityManager mVSManager;
@Mock private VisualStabilityProvider mVSProvider;
@Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardBypassController mBypassController;
@@ -74,7 +74,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
Context context,
HeadsUpManagerLogger headsUpManagerLogger,
NotificationGroupManagerLegacy groupManager,
VisualStabilityManager vsManager,
VisualStabilityProvider visualStabilityProvider,
StatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
ConfigurationController configurationController
@@ -85,9 +85,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
statusBarStateController,
keyguardBypassController,
groupManager,
visualStabilityProvider,
configurationController
);
setup(vsManager);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
}
@@ -103,14 +103,14 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
when(mVSProvider.isReorderingAllowed()).thenReturn(true);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mDependency.injectMockDependency(ConfigurationController.class);
mHeadsUpManager = new TestableHeadsUpManagerPhone(
mContext,
mHeadsUpManagerLogger,
mGroupManager,
mVSManager,
mVSProvider,
mStatusBarStateController,
mBypassController,
mConfigurationController