diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 277b4acb32370..dfdc548ea35e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt new file mode 100644 index 0000000000000..8948969120ace --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt @@ -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() + + /** Keys of any Notifications we've extended the lifetime for, based on guts */ + private val notifsExtendingLifetime = ArraySet() + + /** 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) { + 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) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt new file mode 100644 index 0000000000000..bac5223c1de7a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt @@ -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" + }) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 4d36251579bdf..8c8a8a977d1c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -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; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 47bc444396b98..18f3b457890bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java index f8fe0676e0038..2fe3bd63c2e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt new file mode 100644 index 0000000000000..129f6b1750e69 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt @@ -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) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt new file mode 100644 index 0000000000000..0260f89110f82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt @@ -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?) +} 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 94f5c44d7c78c..dfa1f5f81696a 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 @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 7eec95acc6ec6..8e02d9f635d33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -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 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); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index 39d794dc0bd96..ebeb59177397c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -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(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt new file mode 100644 index 0000000000000..0cba07033c63c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt @@ -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().let { + verify(pipeline).addNotificationLifetimeExtender(it.capture()) + it.value!! + } + notifGutsViewListener = argumentCaptor().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() + } +}