Merge "Convert HeadsUpCoordinator/Test to Kotlin"
This commit is contained in:
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
|
||||
import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;
|
||||
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
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.coordinator.dagger.CoordinatorScope;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController;
|
||||
import com.android.systemui.statusbar.notification.dagger.IncomingHeader;
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
|
||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Coordinates heads up notification (HUN) interactions with the notification pipeline based on
|
||||
* the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
|
||||
* notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
|
||||
* time even though other notifications may be queued to heads up next.
|
||||
*
|
||||
* The current HUN, but not HUNs that are queued to heads up, will be:
|
||||
* - Lifetime extended until it's no longer heads upping.
|
||||
* - Promoted out of its group if it's a child of a group.
|
||||
* - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
|
||||
* - Removed from HeadsUpManager if it's removed from the NotificationCollection.
|
||||
*
|
||||
* Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
|
||||
*/
|
||||
@CoordinatorScope
|
||||
public class HeadsUpCoordinator implements Coordinator {
|
||||
private static final String TAG = "HeadsUpCoordinator";
|
||||
|
||||
private final HeadsUpManager mHeadsUpManager;
|
||||
private final HeadsUpViewBinder mHeadsUpViewBinder;
|
||||
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
|
||||
private final NotificationRemoteInputManager mRemoteInputManager;
|
||||
private final NodeController mIncomingHeaderController;
|
||||
private final DelayableExecutor mExecutor;
|
||||
|
||||
private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
|
||||
// notifs we've extended the lifetime for
|
||||
private final ArraySet<NotificationEntry> mNotifsExtendingLifetime = new ArraySet<>();
|
||||
|
||||
@Inject
|
||||
public HeadsUpCoordinator(
|
||||
HeadsUpManager headsUpManager,
|
||||
HeadsUpViewBinder headsUpViewBinder,
|
||||
NotificationInterruptStateProvider notificationInterruptStateProvider,
|
||||
NotificationRemoteInputManager remoteInputManager,
|
||||
@IncomingHeader NodeController incomingHeaderController,
|
||||
@Main DelayableExecutor executor) {
|
||||
mHeadsUpManager = headsUpManager;
|
||||
mHeadsUpViewBinder = headsUpViewBinder;
|
||||
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
|
||||
mRemoteInputManager = remoteInputManager;
|
||||
mIncomingHeaderController = incomingHeaderController;
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach(NotifPipeline pipeline) {
|
||||
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
|
||||
pipeline.addCollectionListener(mNotifCollectionListener);
|
||||
pipeline.addPromoter(mNotifPromoter);
|
||||
pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
|
||||
}
|
||||
|
||||
public NotifSectioner getSectioner() {
|
||||
return mNotifSectioner;
|
||||
}
|
||||
|
||||
private void onHeadsUpViewBound(NotificationEntry entry) {
|
||||
mHeadsUpManager.showNotification(entry);
|
||||
}
|
||||
|
||||
private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
|
||||
/**
|
||||
* Notification was just added and if it should heads up, bind the view and then show it.
|
||||
*/
|
||||
@Override
|
||||
public void onEntryAdded(NotificationEntry entry) {
|
||||
if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
|
||||
mHeadsUpViewBinder.bindHeadsUpView(
|
||||
entry,
|
||||
HeadsUpCoordinator.this::onHeadsUpViewBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification could've updated to be heads up or not heads up. Even if it did update to
|
||||
* heads up, if the notification specified that it only wants to alert once, don't heads
|
||||
* up again.
|
||||
*/
|
||||
@Override
|
||||
public void onEntryUpdated(NotificationEntry entry) {
|
||||
boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
|
||||
// includes check for whether this notification should be filtered:
|
||||
boolean shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
|
||||
final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
|
||||
if (wasHeadsUp) {
|
||||
if (shouldHeadsUp) {
|
||||
mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
|
||||
} else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
|
||||
// We don't want this to be interrupting anymore, let's remove it
|
||||
mHeadsUpManager.removeNotification(
|
||||
entry.getKey(), false /* removeImmediately */);
|
||||
}
|
||||
} else if (shouldHeadsUp && hunAgain) {
|
||||
// This notification was updated to be heads up, show it!
|
||||
mHeadsUpViewBinder.bindHeadsUpView(
|
||||
entry,
|
||||
HeadsUpCoordinator.this::onHeadsUpViewBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop alerting HUNs that are removed from the notification collection
|
||||
*/
|
||||
@Override
|
||||
public void onEntryRemoved(NotificationEntry entry, int reason) {
|
||||
final String entryKey = entry.getKey();
|
||||
if (mHeadsUpManager.isAlerting(entryKey)) {
|
||||
boolean removeImmediatelyForRemoteInput =
|
||||
mRemoteInputManager.isSpinning(entryKey)
|
||||
&& !FORCE_REMOTE_INPUT_HISTORY;
|
||||
mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntryCleanUp(NotificationEntry entry) {
|
||||
mHeadsUpViewBinder.abortBindCallback(entry);
|
||||
}
|
||||
};
|
||||
|
||||
private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
|
||||
@Override
|
||||
public @NonNull String getName() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
|
||||
mEndLifetimeExtension = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean maybeExtendLifetime(@NonNull NotificationEntry entry, int reason) {
|
||||
boolean extend = !mHeadsUpManager.canRemoveImmediately(entry.getKey());
|
||||
if (extend) {
|
||||
if (isSticky(entry)) {
|
||||
long removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
|
||||
mExecutor.executeDelayed(() -> {
|
||||
if (mNotifsExtendingLifetime.contains(entry)
|
||||
&& mHeadsUpManager.canRemoveImmediately(entry.getKey())) {
|
||||
mHeadsUpManager.removeNotification(
|
||||
entry.getKey(), /* releaseImmediately */ true);
|
||||
}
|
||||
}, removeAfterMillis);
|
||||
} else {
|
||||
// remove as early as possible
|
||||
mExecutor.execute(
|
||||
() -> mHeadsUpManager.removeNotification(
|
||||
entry.getKey(), /* releaseImmediately */ false));
|
||||
}
|
||||
mNotifsExtendingLifetime.add(entry);
|
||||
}
|
||||
return extend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
|
||||
mNotifsExtendingLifetime.remove(entry);
|
||||
}
|
||||
};
|
||||
|
||||
private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
|
||||
@Override
|
||||
public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
|
||||
return isCurrentlyShowingHun(entry);
|
||||
}
|
||||
};
|
||||
|
||||
private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp",
|
||||
NotificationPriorityBucketKt.BUCKET_HEADS_UP) {
|
||||
@Override
|
||||
public boolean isInSection(ListEntry entry) {
|
||||
return isCurrentlyShowingHun(entry);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public NodeController getHeaderNodeController() {
|
||||
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
|
||||
if (RankingCoordinator.SHOW_ALL_SECTIONS) {
|
||||
return mIncomingHeaderController;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
|
||||
new OnHeadsUpChangedListener() {
|
||||
@Override
|
||||
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
|
||||
if (!isHeadsUp) {
|
||||
mHeadsUpViewBinder.unbindHeadsUpView(entry);
|
||||
endNotifLifetimeExtensionIfExtended(entry);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean isSticky(NotificationEntry entry) {
|
||||
return mHeadsUpManager.isSticky(entry.getKey());
|
||||
}
|
||||
|
||||
private boolean isCurrentlyShowingHun(ListEntry entry) {
|
||||
return mHeadsUpManager.isAlerting(entry.getKey());
|
||||
}
|
||||
|
||||
private void endNotifLifetimeExtensionIfExtended(NotificationEntry entry) {
|
||||
if (mNotifsExtendingLifetime.remove(entry)) {
|
||||
mEndLifetimeExtension.onEndLifetimeExtension(mLifetimeExtender, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.util.ArraySet
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager
|
||||
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.coordinator.dagger.CoordinatorScope
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController
|
||||
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
|
||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Coordinates heads up notification (HUN) interactions with the notification pipeline based on
|
||||
* the HUN state reported by the [HeadsUpManager]. In this class we only consider one
|
||||
* notification, in particular the [HeadsUpManager.getTopEntry], to be HeadsUpping at a
|
||||
* time even though other notifications may be queued to heads up next.
|
||||
*
|
||||
* The current HUN, but not HUNs that are queued to heads up, will be:
|
||||
* - Lifetime extended until it's no longer heads upping.
|
||||
* - Promoted out of its group if it's a child of a group.
|
||||
* - In the HeadsUpCoordinatorSection. Ordering is configured in [NotifCoordinators].
|
||||
* - Removed from HeadsUpManager if it's removed from the NotificationCollection.
|
||||
*
|
||||
* Note: The inflation callback in [PreparationCoordinator] handles showing HUNs.
|
||||
*/
|
||||
@CoordinatorScope
|
||||
class HeadsUpCoordinator @Inject constructor(
|
||||
private val mHeadsUpManager: HeadsUpManager,
|
||||
private val mHeadsUpViewBinder: HeadsUpViewBinder,
|
||||
private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
|
||||
private val mRemoteInputManager: NotificationRemoteInputManager,
|
||||
@IncomingHeader private val mIncomingHeaderController: NodeController,
|
||||
@Main private val mExecutor: DelayableExecutor
|
||||
) : Coordinator {
|
||||
private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
|
||||
|
||||
// notifs we've extended the lifetime for
|
||||
private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
|
||||
|
||||
override fun attach(pipeline: NotifPipeline) {
|
||||
mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
|
||||
pipeline.addCollectionListener(mNotifCollectionListener)
|
||||
pipeline.addPromoter(mNotifPromoter)
|
||||
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
|
||||
}
|
||||
|
||||
private fun onHeadsUpViewBound(entry: NotificationEntry) {
|
||||
mHeadsUpManager.showNotification(entry)
|
||||
}
|
||||
|
||||
private val mNotifCollectionListener = object : NotifCollectionListener {
|
||||
/**
|
||||
* Notification was just added and if it should heads up, bind the view and then show it.
|
||||
*/
|
||||
override fun onEntryAdded(entry: NotificationEntry) {
|
||||
if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
|
||||
mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification could've updated to be heads up or not heads up. Even if it did update to
|
||||
* heads up, if the notification specified that it only wants to alert once, don't heads
|
||||
* up again.
|
||||
*/
|
||||
override fun onEntryUpdated(entry: NotificationEntry) {
|
||||
val hunAgain = HeadsUpController.alertAgain(entry, entry.sbn.notification)
|
||||
// includes check for whether this notification should be filtered:
|
||||
val shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
|
||||
val wasHeadsUp = mHeadsUpManager.isAlerting(entry.key)
|
||||
if (wasHeadsUp) {
|
||||
if (shouldHeadsUp) {
|
||||
mHeadsUpManager.updateNotification(entry.key, hunAgain)
|
||||
} else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
|
||||
// We don't want this to be interrupting anymore, let's remove it
|
||||
mHeadsUpManager.removeNotification(
|
||||
entry.key, false /* removeImmediately */
|
||||
)
|
||||
}
|
||||
} else if (shouldHeadsUp && hunAgain) {
|
||||
// This notification was updated to be heads up, show it!
|
||||
mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop alerting HUNs that are removed from the notification collection
|
||||
*/
|
||||
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
|
||||
val entryKey = entry.key
|
||||
if (mHeadsUpManager.isAlerting(entryKey)) {
|
||||
val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
|
||||
!NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
|
||||
mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEntryCleanUp(entry: NotificationEntry) {
|
||||
mHeadsUpViewBinder.abortBindCallback(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private val mLifetimeExtender = object : NotifLifetimeExtender {
|
||||
override fun getName() = TAG
|
||||
|
||||
override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
|
||||
mEndLifetimeExtension = callback
|
||||
}
|
||||
|
||||
override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
|
||||
if (mHeadsUpManager.canRemoveImmediately(entry.key)) {
|
||||
return false
|
||||
}
|
||||
if (isSticky(entry)) {
|
||||
val removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
|
||||
mExecutor.executeDelayed({
|
||||
val canStillRemove = mHeadsUpManager.canRemoveImmediately(entry.key)
|
||||
if (mNotifsExtendingLifetime.contains(entry) && canStillRemove) {
|
||||
mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ true)
|
||||
}
|
||||
}, removeAfterMillis)
|
||||
} else {
|
||||
mExecutor.execute {
|
||||
mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ false)
|
||||
}
|
||||
}
|
||||
mNotifsExtendingLifetime.add(entry)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun cancelLifetimeExtension(entry: NotificationEntry) {
|
||||
mNotifsExtendingLifetime.remove(entry)
|
||||
}
|
||||
}
|
||||
|
||||
private val mNotifPromoter = object : NotifPromoter(TAG) {
|
||||
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
|
||||
isCurrentlyShowingHun(entry)
|
||||
}
|
||||
|
||||
val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
|
||||
override fun isInSection(entry: ListEntry): Boolean = isCurrentlyShowingHun(entry)
|
||||
|
||||
override fun getHeaderNodeController(): NodeController? =
|
||||
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
|
||||
if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
|
||||
}
|
||||
|
||||
private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener {
|
||||
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
|
||||
if (!isHeadsUp) {
|
||||
mHeadsUpViewBinder.unbindHeadsUpView(entry)
|
||||
endNotifLifetimeExtensionIfExtended(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
|
||||
|
||||
private fun isCurrentlyShowingHun(entry: ListEntry) = mHeadsUpManager.isAlerting(entry.key)
|
||||
|
||||
private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
|
||||
if (mNotifsExtendingLifetime.remove(entry)) {
|
||||
mEndLifetimeExtension?.onEndLifetimeExtension(mLifetimeExtender, entry)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "HeadsUpCoordinator"
|
||||
}
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
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.NotifPromoter;
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController;
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
|
||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
|
||||
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class HeadsUpCoordinatorTest extends SysuiTestCase {
|
||||
|
||||
private HeadsUpCoordinator mCoordinator;
|
||||
|
||||
// captured listeners and pluggables:
|
||||
private NotifCollectionListener mCollectionListener;
|
||||
private NotifPromoter mNotifPromoter;
|
||||
private NotifLifetimeExtender mNotifLifetimeExtender;
|
||||
private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
|
||||
private NotifSectioner mNotifSectioner;
|
||||
|
||||
@Mock private NotifPipeline mNotifPipeline;
|
||||
@Mock private HeadsUpManager mHeadsUpManager;
|
||||
@Mock private HeadsUpViewBinder mHeadsUpViewBinder;
|
||||
@Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
|
||||
@Mock private NotificationRemoteInputManager mRemoteInputManager;
|
||||
@Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
|
||||
@Mock private NodeController mHeaderController;
|
||||
|
||||
private NotificationEntry mEntry;
|
||||
private final FakeSystemClock mClock = new FakeSystemClock();
|
||||
private final FakeExecutor mExecutor = new FakeExecutor(mClock);
|
||||
private final ArrayList<NotificationEntry> mHuns = new ArrayList();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mCoordinator = new HeadsUpCoordinator(
|
||||
mHeadsUpManager,
|
||||
mHeadsUpViewBinder,
|
||||
mNotificationInterruptStateProvider,
|
||||
mRemoteInputManager,
|
||||
mHeaderController,
|
||||
mExecutor);
|
||||
|
||||
mCoordinator.attach(mNotifPipeline);
|
||||
|
||||
// capture arguments:
|
||||
ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
|
||||
ArgumentCaptor.forClass(NotifCollectionListener.class);
|
||||
ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
|
||||
ArgumentCaptor.forClass(NotifPromoter.class);
|
||||
ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
|
||||
ArgumentCaptor.forClass(NotifLifetimeExtender.class);
|
||||
ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
|
||||
ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
|
||||
|
||||
verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
|
||||
verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
|
||||
verify(mNotifPipeline).addNotificationLifetimeExtender(
|
||||
notifLifetimeExtenderCaptor.capture());
|
||||
verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
|
||||
|
||||
given(mHeadsUpManager.getAllEntries()).willAnswer(i -> mHuns.stream());
|
||||
given(mHeadsUpManager.isAlerting(anyString())).willAnswer(i -> {
|
||||
String key = i.getArgument(0);
|
||||
for (NotificationEntry entry : mHuns) {
|
||||
if (entry.getKey().equals(key)) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L);
|
||||
|
||||
mCollectionListener = notifCollectionCaptor.getValue();
|
||||
mNotifPromoter = notifPromoterCaptor.getValue();
|
||||
mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
|
||||
mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
|
||||
|
||||
mNotifSectioner = mCoordinator.getSectioner();
|
||||
mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
|
||||
mEntry = new NotificationEntryBuilder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelStickyNotification() {
|
||||
when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
|
||||
addHUN(mEntry);
|
||||
when(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true);
|
||||
when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L);
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
|
||||
mClock.advanceTime(1000L);
|
||||
mExecutor.runAllReady();
|
||||
verify(mHeadsUpManager, times(0))
|
||||
.removeNotification(anyString(), eq(false));
|
||||
verify(mHeadsUpManager, times(1))
|
||||
.removeNotification(anyString(), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelUpdatedStickyNotification() {
|
||||
when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
|
||||
addHUN(mEntry);
|
||||
when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
|
||||
mClock.advanceTime(1000L);
|
||||
mExecutor.runAllReady();
|
||||
verify(mHeadsUpManager, times(0))
|
||||
.removeNotification(anyString(), eq(false));
|
||||
verify(mHeadsUpManager, times(0))
|
||||
.removeNotification(anyString(), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelNotification() {
|
||||
when(mHeadsUpManager.isSticky(anyString())).thenReturn(false);
|
||||
addHUN(mEntry);
|
||||
when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
|
||||
mClock.advanceTime(1000L);
|
||||
mExecutor.runAllReady();
|
||||
verify(mHeadsUpManager, times(1))
|
||||
.removeNotification(anyString(), eq(false));
|
||||
verify(mHeadsUpManager, times(0))
|
||||
.removeNotification(anyString(), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPromotesCurrentHUN() {
|
||||
// GIVEN the current HUN is set to mEntry
|
||||
addHUN(mEntry);
|
||||
|
||||
// THEN only promote the current HUN, mEntry
|
||||
assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
|
||||
assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder()
|
||||
.setPkg("test-package2")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeInSectionCurrentHUN() {
|
||||
// GIVEN the current HUN is set to mEntry
|
||||
addHUN(mEntry);
|
||||
|
||||
// THEN only section the current HUN, mEntry
|
||||
assertTrue(mNotifSectioner.isInSection(mEntry));
|
||||
assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder()
|
||||
.setPkg("test-package")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifetimeExtendsCurrentHUN() {
|
||||
// GIVEN there is a HUN, mEntry
|
||||
addHUN(mEntry);
|
||||
|
||||
given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer(i -> {
|
||||
String key = i.getArgument(0);
|
||||
for (NotificationEntry entry : mHuns) {
|
||||
if (entry.getKey().equals(key)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// THEN only the current HUN, mEntry, should be lifetimeExtended
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0));
|
||||
assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
|
||||
new NotificationEntryBuilder()
|
||||
.setPkg("test-package")
|
||||
.build(), /* cancellationReason */ 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowHUNOnInflationFinished() {
|
||||
// WHEN a notification should HUN and its inflation is finished
|
||||
when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
|
||||
|
||||
ArgumentCaptor<BindCallback> bindCallbackCaptor =
|
||||
ArgumentCaptor.forClass(BindCallback.class);
|
||||
mCollectionListener.onEntryAdded(mEntry);
|
||||
verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), bindCallbackCaptor.capture());
|
||||
|
||||
bindCallbackCaptor.getValue().onBindFinished(mEntry);
|
||||
|
||||
// THEN we tell the HeadsUpManager to show the notification
|
||||
verify(mHeadsUpManager).showNotification(mEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoHUNOnInflationFinished() {
|
||||
// WHEN a notification shouldn't HUN and its inflation is finished
|
||||
when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
|
||||
ArgumentCaptor<BindCallback> bindCallbackCaptor =
|
||||
ArgumentCaptor.forClass(BindCallback.class);
|
||||
mCollectionListener.onEntryAdded(mEntry);
|
||||
|
||||
// THEN we never bind the heads up view or tell HeadsUpManager to show the notification
|
||||
verify(mHeadsUpViewBinder, never()).bindHeadsUpView(
|
||||
eq(mEntry), bindCallbackCaptor.capture());
|
||||
verify(mHeadsUpManager, never()).showNotification(mEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnEntryRemovedRemovesHeadsUpNotification() {
|
||||
// GIVEN the current HUN is mEntry
|
||||
addHUN(mEntry);
|
||||
|
||||
// WHEN mEntry is removed from the notification collection
|
||||
mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
|
||||
when(mRemoteInputManager.isSpinning(any())).thenReturn(false);
|
||||
|
||||
// THEN heads up manager should remove the entry
|
||||
verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
|
||||
}
|
||||
|
||||
private void addHUN(NotificationEntry entry) {
|
||||
mHuns.add(entry);
|
||||
when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
|
||||
mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager
|
||||
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.NotifPromoter
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
|
||||
import com.android.systemui.statusbar.notification.collection.render.NodeController
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
|
||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
|
||||
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.withArgCaptor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.BDDMockito.given
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.util.ArrayList
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper
|
||||
class HeadsUpCoordinatorTest : SysuiTestCase() {
|
||||
private lateinit var mCoordinator: HeadsUpCoordinator
|
||||
|
||||
// captured listeners and pluggables:
|
||||
private lateinit var mCollectionListener: NotifCollectionListener
|
||||
private lateinit var mNotifPromoter: NotifPromoter
|
||||
private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
|
||||
private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
|
||||
private lateinit var mNotifSectioner: NotifSectioner
|
||||
|
||||
private val mNotifPipeline: NotifPipeline = mock()
|
||||
private val mHeadsUpManager: HeadsUpManager = mock()
|
||||
private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
|
||||
private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
|
||||
private val mRemoteInputManager: NotificationRemoteInputManager = mock()
|
||||
private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
|
||||
private val mHeaderController: NodeController = mock()
|
||||
|
||||
private lateinit var mEntry: NotificationEntry
|
||||
private val mExecutor = FakeExecutor(FakeSystemClock())
|
||||
private val mHuns: ArrayList<NotificationEntry> = ArrayList()
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
mCoordinator = HeadsUpCoordinator(
|
||||
mHeadsUpManager,
|
||||
mHeadsUpViewBinder,
|
||||
mNotificationInterruptStateProvider,
|
||||
mRemoteInputManager,
|
||||
mHeaderController,
|
||||
mExecutor)
|
||||
mCoordinator.attach(mNotifPipeline)
|
||||
|
||||
// capture arguments:
|
||||
mCollectionListener = withArgCaptor {
|
||||
verify(mNotifPipeline).addCollectionListener(capture())
|
||||
}
|
||||
mNotifPromoter = withArgCaptor {
|
||||
verify(mNotifPipeline).addPromoter(capture())
|
||||
}
|
||||
mNotifLifetimeExtender = withArgCaptor {
|
||||
verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
|
||||
}
|
||||
mOnHeadsUpChangedListener = withArgCaptor {
|
||||
verify(mHeadsUpManager).addListener(capture())
|
||||
}
|
||||
given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() }
|
||||
given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation ->
|
||||
val key = invocation.getArgument<String>(0)
|
||||
mHuns.any { entry -> entry.key == key }
|
||||
}
|
||||
given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer { invocation ->
|
||||
val key = invocation.getArgument<String>(0)
|
||||
!mHuns.any { entry -> entry.key == key }
|
||||
}
|
||||
whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
|
||||
mNotifSectioner = mCoordinator.sectioner
|
||||
mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
|
||||
mEntry = NotificationEntryBuilder().build()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCancelStickyNotification() {
|
||||
whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
|
||||
addHUN(mEntry)
|
||||
whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true)
|
||||
whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L)
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
|
||||
mExecutor.advanceClockToLast()
|
||||
mExecutor.runAllReady()
|
||||
verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
|
||||
verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCancelUpdatedStickyNotification() {
|
||||
whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
|
||||
addHUN(mEntry)
|
||||
whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
|
||||
mExecutor.advanceClockToLast()
|
||||
mExecutor.runAllReady()
|
||||
verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
|
||||
verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCancelNotification() {
|
||||
whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(false)
|
||||
addHUN(mEntry)
|
||||
whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
|
||||
mExecutor.advanceClockToLast()
|
||||
mExecutor.runAllReady()
|
||||
verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(false))
|
||||
verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPromotesCurrentHUN() {
|
||||
// GIVEN the current HUN is set to mEntry
|
||||
addHUN(mEntry)
|
||||
|
||||
// THEN only promote the current HUN, mEntry
|
||||
assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
|
||||
assertFalse(mNotifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
|
||||
.setPkg("test-package2")
|
||||
.build()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncludeInSectionCurrentHUN() {
|
||||
// GIVEN the current HUN is set to mEntry
|
||||
addHUN(mEntry)
|
||||
|
||||
// THEN only section the current HUN, mEntry
|
||||
assertTrue(mNotifSectioner.isInSection(mEntry))
|
||||
assertFalse(mNotifSectioner.isInSection(NotificationEntryBuilder()
|
||||
.setPkg("test-package")
|
||||
.build()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLifetimeExtendsCurrentHUN() {
|
||||
// GIVEN there is a HUN, mEntry
|
||||
addHUN(mEntry)
|
||||
|
||||
// THEN only the current HUN, mEntry, should be lifetimeExtended
|
||||
assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0))
|
||||
assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
|
||||
NotificationEntryBuilder()
|
||||
.setPkg("test-package")
|
||||
.build(), /* cancellationReason */ 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowHUNOnInflationFinished() {
|
||||
// WHEN a notification should HUN and its inflation is finished
|
||||
whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
|
||||
|
||||
mCollectionListener.onEntryAdded(mEntry)
|
||||
withArgCaptor<BindCallback> {
|
||||
verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
|
||||
}.onBindFinished(mEntry)
|
||||
|
||||
// THEN we tell the HeadsUpManager to show the notification
|
||||
verify(mHeadsUpManager).showNotification(mEntry)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoHUNOnInflationFinished() {
|
||||
// WHEN a notification shouldn't HUN and its inflation is finished
|
||||
whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
|
||||
mCollectionListener.onEntryAdded(mEntry)
|
||||
|
||||
// THEN we never bind the heads up view or tell HeadsUpManager to show the notification
|
||||
verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
|
||||
verify(mHeadsUpManager, never()).showNotification(mEntry)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnEntryRemovedRemovesHeadsUpNotification() {
|
||||
// GIVEN the current HUN is mEntry
|
||||
addHUN(mEntry)
|
||||
|
||||
// WHEN mEntry is removed from the notification collection
|
||||
mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0)
|
||||
whenever(mRemoteInputManager.isSpinning(any())).thenReturn(false)
|
||||
|
||||
// THEN heads up manager should remove the entry
|
||||
verify(mHeadsUpManager).removeNotification(mEntry.key, false)
|
||||
}
|
||||
|
||||
private fun addHUN(entry: NotificationEntry) {
|
||||
mHuns.add(entry)
|
||||
whenever(mHeadsUpManager.topEntry).thenReturn(entry)
|
||||
mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user