New Pipeline: Implement lifetime extension for guts

Fixes: 201413978
Test: GutsCoordinatorTest
Change-Id: Icaeb83fc41bf92328e66b3a9a6ecc5ba23e515d9
This commit is contained in:
Jeff DeCew
2021-09-28 20:33:11 +00:00
parent 24dd3d748f
commit d5b52e01d1
12 changed files with 386 additions and 21 deletions

View File

@@ -523,7 +523,9 @@ public class NotifCollection implements Dumpable {
}
}
private void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry) {
private void onEndLifetimeExtension(
@NonNull NotifLifetimeExtender extender,
@NonNull NotificationEntry entry) {
Assert.isMainThread();
if (!mAttached) {
return;

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArraySet
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
import com.android.systemui.statusbar.notification.row.NotificationGuts
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
private const val TAG = "GutsCoordinator"
/**
* Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline.
* Specifically, this just adds the lifetime extension necessary to keep guts from disappearing.
*/
@SysUISingleton
class GutsCoordinator @Inject constructor(
private val notifGutsViewManager: NotifGutsViewManager,
private val logger: GutsCoordinatorLogger,
dumpManager: DumpManager
) : Coordinator, Dumpable {
/** Keys of any Notifications for which we've been told the guts are open */
private val notifsWithOpenGuts = ArraySet<String>()
/** Keys of any Notifications we've extended the lifetime for, based on guts */
private val notifsExtendingLifetime = ArraySet<String>()
/** Callback for ending lifetime extension */
private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null
init {
dumpManager.registerDumpable(TAG, this)
}
override fun attach(pipeline: NotifPipeline) {
notifGutsViewManager.setGutsListener(mGutsListener)
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
pw.println(" notifsWithOpenGuts: ${notifsWithOpenGuts.size}")
for (key in notifsWithOpenGuts) {
pw.println(" * $key")
}
pw.println(" notifsExtendingLifetime: ${notifsExtendingLifetime.size}")
for (key in notifsExtendingLifetime) {
pw.println(" * $key")
}
pw.println(" onEndLifetimeExtensionCallback: $onEndLifetimeExtensionCallback")
}
private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender {
override fun getName(): String {
return TAG
}
override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
onEndLifetimeExtensionCallback = callback
}
override fun shouldExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
val isShowingGuts = isCurrentlyShowingGuts(entry)
if (isShowingGuts) {
notifsExtendingLifetime.add(entry.key)
}
return isShowingGuts
}
override fun cancelLifetimeExtension(entry: NotificationEntry) {
notifsExtendingLifetime.remove(entry.key)
}
}
private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener {
override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) {
logger.logGutsOpened(entry.key, guts)
if (guts.isLeavebehind) {
// leave-behind guts should not extend the lifetime of the notification
closeGutsAndEndLifetimeExtension(entry)
} else {
notifsWithOpenGuts.add(entry.key)
}
}
override fun onGutsClose(entry: NotificationEntry) {
logger.logGutsClosed(entry.key)
closeGutsAndEndLifetimeExtension(entry)
}
}
private fun isCurrentlyShowingGuts(entry: ListEntry) =
notifsWithOpenGuts.contains(entry.key)
private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
notifsWithOpenGuts.remove(entry.key)
if (notifsExtendingLifetime.remove(entry.key)) {
onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry)
}
}
}

View File

@@ -0,0 +1,32 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.row.NotificationGuts
import javax.inject.Inject
private const val TAG = "GutsCoordinator"
class GutsCoordinatorLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
fun logGutsOpened(key: String, guts: NotificationGuts) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
str2 = guts::class.simpleName
bool1 = guts.isLeavebehind
}, {
"Guts of type $str2 (leave behind: $bool1) opened for class $str1"
})
}
fun logGutsClosed(key: String) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
}, {
"Guts closed for class $str1"
})
}
}

View File

@@ -19,7 +19,8 @@ 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.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -164,17 +165,17 @@ public class HeadsUpCoordinator implements Coordinator {
private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
@Override
public String getName() {
public @NonNull String getName() {
return TAG;
}
@Override
public void setCallback(OnEndLifetimeExtensionCallback callback) {
public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
mEndLifetimeExtension = callback;
}
@Override
public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) {
boolean isShowingHun = isCurrentlyShowingHun(entry);
if (isShowingHun) {
mNotifExtendingLifetime = entry;
@@ -183,7 +184,7 @@ public class HeadsUpCoordinator implements Coordinator {
}
@Override
public void cancelLifetimeExtension(NotificationEntry entry) {
public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
if (Objects.equals(mNotifExtendingLifetime, entry)) {
mNotifExtendingLifetime = null;
}

View File

@@ -57,6 +57,7 @@ public class NotifCoordinators implements Dumpable {
DeviceProvisionedCoordinator deviceProvisionedCoordinator,
BubbleCoordinator bubbleCoordinator,
HeadsUpCoordinator headsUpCoordinator,
GutsCoordinator gutsCoordinator,
ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
@@ -83,6 +84,7 @@ public class NotifCoordinators implements Dumpable {
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
mCoordinators.add(headsUpCoordinator);
mCoordinators.add(gutsCoordinator);
mCoordinators.add(preparationCoordinator);
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
import androidx.annotation.NonNull;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -26,14 +28,14 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
*/
public interface NotifLifetimeExtender {
/** Name to associate with this extender (for the purposes of debugging) */
String getName();
@NonNull String getName();
/**
* Called on the extender immediately after it has been registered. The extender should hang on
* to this callback and execute it whenever it no longer needs to extend the lifetime of a
* notification.
*/
void setCallback(OnEndLifetimeExtensionCallback callback);
void setCallback(@NonNull OnEndLifetimeExtensionCallback callback);
/**
* Called by the NotifCollection whenever a notification has been retracted (by the app) or
@@ -43,7 +45,7 @@ public interface NotifLifetimeExtender {
* called on all lifetime extenders even if earlier ones return true (in other words, multiple
* lifetime extenders can be extending a notification at the same time).
*/
boolean shouldExtendLifetime(NotificationEntry entry, @CancellationReason int reason);
boolean shouldExtendLifetime(@NonNull NotificationEntry entry, @CancellationReason int reason);
/**
* Called by the NotifCollection to inform a lifetime extender that its extension of a notif
@@ -51,7 +53,7 @@ public interface NotifLifetimeExtender {
* lifetime extension). The extender should clean up any references it has to the notif in
* question.
*/
void cancelLifetimeExtension(NotificationEntry entry);
void cancelLifetimeExtension(@NonNull NotificationEntry entry);
/** Callback for notifying the NotifCollection that a lifetime extension has expired.*/
interface OnEndLifetimeExtensionCallback {
@@ -59,6 +61,8 @@ public interface NotifLifetimeExtender {
* Stop extending the lifetime of `entry` with `extender` and then immediately re-evaluates
* whether to continue lifetime extending this notification or to remove it.
*/
void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry);
void onEndLifetimeExtension(
@NonNull NotifLifetimeExtender extender,
@NonNull NotificationEntry entry);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.NotificationGuts
/**
* Interface for listening to guts open and close events.
*/
interface NotifGutsViewListener {
/** A notification's guts are being opened */
fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts)
/** A notification's guts are being closed */
fun onGutsClose(entry: NotificationEntry)
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.render
/** A type which provides open and close guts events to a single listener */
interface NotifGutsViewManager {
/**
* @param listener the object that will listen to open and close guts events
*/
fun setGutsListener(listener: NotifGutsViewListener?)
}

View File

@@ -58,6 +58,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -169,6 +170,14 @@ public interface NotificationsModule {
dumpManager);
}
/** Provides an instance of {@link NotifGutsViewManager} */
@SysUISingleton
@Provides
static NotifGutsViewManager provideNotifGutsViewManager(
NotificationGutsManager notificationGutsManager) {
return notificationGutsManager;
}
/** Provides an instance of {@link VisualStabilityManager} */
@SysUISingleton
@Provides

View File

@@ -65,6 +65,8 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -83,7 +85,8 @@ import dagger.Lazy;
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender,
NotifGutsViewManager {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -123,7 +126,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final Optional<BubblesManager> mBubblesManagerOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
private final NotificationEntryManager mNotificationEntryManager;
private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private final LauncherApps mLauncherApps;
private final ShortcutManager mShortcutManager;
@@ -131,6 +133,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final UiEventLogger mUiEventLogger;
private final ShadeController mShadeController;
private final AppWidgetManager mAppWidgetManager;
private NotifGutsViewListener mGutsListener;
/**
* Injected constructor. See {@link NotificationsModule}.
@@ -161,7 +164,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mAccessibilityManager = accessibilityManager;
mHighPriorityProvider = highPriorityProvider;
mNotificationManager = notificationManager;
mNotificationEntryManager = notificationEntryManager;
mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
mLauncherApps = launcherApps;
mShortcutManager = shortcutManager;
@@ -258,10 +260,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
StatusBarNotification sbn = row.getEntry().getSbn();
NotificationEntry entry = row.getEntry();
row.setGutsView(item);
row.setTag(sbn.getPackageName());
row.setTag(entry.getSbn().getPackageName());
row.getGuts().setClosedListener((NotificationGuts g) -> {
row.onGutsClosed();
if (!g.willBeRemoved() && !row.isRemoved()) {
@@ -272,7 +274,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mNotificationGutsExposed = null;
mGutsMenuItem = null;
}
String key = sbn.getKey();
if (mGutsListener != null) {
mGutsListener.onGutsClose(entry);
}
String key = entry.getKey();
if (key.equals(mKeyToRemoveOnGutsClosed)) {
mKeyToRemoveOnGutsClosed = null;
if (mNotificationLifetimeFinishedCallback != null) {
@@ -650,6 +655,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
needsFalsingProtection,
row::onGutsOpened);
if (mGutsListener != null) {
mGutsListener.onGutsOpen(row.getEntry(), guts);
}
row.closeRemoteInput();
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mGutsMenuItem = menuItem;
@@ -695,10 +704,17 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NotificationGutsManager state:");
pw.print(" mKeyToRemoveOnGutsClosed: ");
pw.print(" mKeyToRemoveOnGutsClosed (legacy): ");
pw.println(mKeyToRemoveOnGutsClosed);
}
/**
* @param gutsListener the listener for open and close guts events
*/
public void setGutsListener(NotifGutsViewListener gutsListener) {
mGutsListener = gutsListener;
}
public interface OnSettingsClickListener {
public void onSettingsClick(String key);
}

View File

@@ -60,6 +60,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -1405,25 +1406,26 @@ public class NotifCollectionTest extends SysuiTestCase {
mName = name;
}
@NonNull
@Override
public String getName() {
return mName;
}
@Override
public void setCallback(OnEndLifetimeExtensionCallback callback) {
public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
this.callback = callback;
}
@Override
public boolean shouldExtendLifetime(
NotificationEntry entry,
@NonNull NotificationEntry entry,
@CancellationReason int reason) {
return shouldExtendLifetime;
}
@Override
public void cancelLifetimeExtension(NotificationEntry entry) {
public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
if (onCancelLifetimeExtension != null) {
onCancelLifetimeExtension.run();
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.notification.collection.coordinator
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
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.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import com.android.systemui.statusbar.notification.row.NotificationGuts
import com.android.systemui.util.mockito.argumentCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class GutsCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: GutsCoordinator
private lateinit var notifLifetimeExtender: NotifLifetimeExtender
private lateinit var notifGutsViewListener: NotifGutsViewListener
private lateinit var entry1: NotificationEntry
private lateinit var entry2: NotificationEntry
@Mock private lateinit var notifGutsViewManager: NotifGutsViewManager
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var logger: GutsCoordinatorLogger
@Mock private lateinit var lifetimeExtenderCallback: OnEndLifetimeExtensionCallback
@Before
fun setUp() {
initMocks(this)
coordinator = GutsCoordinator(notifGutsViewManager, logger, dumpManager)
coordinator.attach(pipeline)
notifLifetimeExtender = argumentCaptor<NotifLifetimeExtender>().let {
verify(pipeline).addNotificationLifetimeExtender(it.capture())
it.value!!
}
notifGutsViewListener = argumentCaptor<NotifGutsViewListener>().let {
verify(notifGutsViewManager).setGutsListener(it.capture())
it.value!!
}
notifLifetimeExtender.setCallback(lifetimeExtenderCallback)
entry1 = NotificationEntryBuilder().setId(1).build()
entry2 = NotificationEntryBuilder().setId(2).build()
}
@Test
fun testSimpleLifetimeExtension() {
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
}
@Test
fun testDoubleOpenLifetimeExtension() {
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
}
@Test
fun testTwoEntryLifetimeExtension() {
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry2, mock(NotificationGuts::class.java))
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry2)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry2)
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
}
}