diff options
6 files changed, 139 insertions, 67 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 8a31ed9271ad..1e7e7f08a752 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga 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.provider.LaunchFullScreenIntentProvider 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 @@ -68,6 +69,7 @@ class HeadsUpCoordinator @Inject constructor( private val mHeadsUpViewBinder: HeadsUpViewBinder, private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider, private val mRemoteInputManager: NotificationRemoteInputManager, + private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider, @IncomingHeader private val mIncomingHeaderController: NodeController, @Main private val mExecutor: DelayableExecutor, ) : Coordinator { @@ -373,6 +375,12 @@ class HeadsUpCoordinator @Inject constructor( * Notification was just added and if it should heads up, bind the view and then show it. */ override fun onEntryAdded(entry: NotificationEntry) { + // First check whether this notification should launch a full screen intent, and + // launch it if needed. + if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { + mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry) + } + // shouldHeadsUp includes check for whether this notification should be filtered val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry) mPostedEntries[entry.key] = PostedEntry( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt new file mode 100644 index 000000000000..74ff78e25a2f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.provider + +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.util.ListenerSet +import javax.inject.Inject + +/** + * A class that enables communication of decisions to launch a notification's full screen intent. + */ +@SysUISingleton +class LaunchFullScreenIntentProvider @Inject constructor() { + companion object { + private const val TAG = "LaunchFullScreenIntentProvider" + } + private val listeners = ListenerSet<Listener>() + + /** + * Registers a listener with this provider. These listeners will be alerted whenever a full + * screen intent should be launched for a notification entry. + */ + fun registerListener(listener: Listener) { + listeners.addIfAbsent(listener) + } + + /** Removes the specified listener. */ + fun removeListener(listener: Listener) { + listeners.remove(listener) + } + + /** + * Sends a request to launch full screen intent for the given notification entry to all + * registered listeners. + */ + fun launchFullScreenIntent(entry: NotificationEntry) { + if (listeners.isEmpty()) { + // This should never happen, but we should definitely know if it does because having + // no listeners would indicate that FSIs are getting entirely dropped on the floor. + Log.wtf(TAG, "no listeners found when launchFullScreenIntent requested") + } + for (listener in listeners) { + listener.onFullScreenIntentRequested(entry) + } + } + + /** Listener interface for passing full screen intent launch decisions. */ + fun interface Listener { + /** + * Invoked whenever a full screen intent launch is requested for the given notification + * entry. + */ + fun onFullScreenIntentRequested(entry: NotificationEntry) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 5cd2ba1b1cf3..b6ae4a088880 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -23,7 +23,6 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOp import android.app.ActivityManager; import android.app.KeyguardManager; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.Context; @@ -60,9 +59,8 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -126,7 +124,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte Context context, Handler mainThreadHandler, Executor uiBgExecutor, - NotifPipeline notifPipeline, NotificationVisibilityProvider visibilityProvider, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, @@ -151,7 +148,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte NotificationPresenter presenter, NotificationPanelViewController panel, ActivityLaunchAnimator activityLaunchAnimator, - NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) { + NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, + LaunchFullScreenIntentProvider launchFullScreenIntentProvider) { mContext = context; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; @@ -182,12 +180,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; - notifPipeline.addCollectionListener(new NotifCollectionListener() { - @Override - public void onEntryAdded(NotificationEntry entry) { - handleFullScreenIntent(entry); - } - }); + launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); } /** @@ -549,38 +542,36 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte } @VisibleForTesting - void handleFullScreenIntent(NotificationEntry entry) { - if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { - if (shouldSuppressFullScreenIntent(entry)) { - mLogger.logFullScreenIntentSuppressedByDnD(entry); - } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) { - mLogger.logFullScreenIntentNotImportantEnough(entry); - } else { - // Stop screensaver if the notification has a fullscreen intent. - // (like an incoming phone call) - mUiBgExecutor.execute(() -> { - try { - mDreamManager.awaken(); - } catch (RemoteException e) { - e.printStackTrace(); - } - }); + void launchFullScreenIntent(NotificationEntry entry) { + // Skip if device is in VR mode. + if (mPresenter.isDeviceInVrMode()) { + mLogger.logFullScreenIntentSuppressedByVR(entry); + return; + } - // not immersive & a fullscreen alert should be shown - final PendingIntent fullscreenIntent = - entry.getSbn().getNotification().fullScreenIntent; - mLogger.logSendingFullScreenIntent(entry, fullscreenIntent); - try { - EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, - entry.getKey()); - mCentralSurfaces.wakeUpForFullScreenIntent(); - fullscreenIntent.send(); - entry.notifyFullScreenIntentLaunched(); - mMetricsLogger.count("note_fullscreen", 1); - } catch (PendingIntent.CanceledException e) { - // ignore - } + // Stop screensaver if the notification has a fullscreen intent. + // (like an incoming phone call) + mUiBgExecutor.execute(() -> { + try { + mDreamManager.awaken(); + } catch (RemoteException e) { + e.printStackTrace(); } + }); + + // not immersive & a fullscreen alert should be shown + final PendingIntent fullscreenIntent = + entry.getSbn().getNotification().fullScreenIntent; + mLogger.logSendingFullScreenIntent(entry, fullscreenIntent); + try { + EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, + entry.getKey()); + mCentralSurfaces.wakeUpForFullScreenIntent(); + fullscreenIntent.send(); + entry.notifyFullScreenIntentLaunched(); + mMetricsLogger.count("note_fullscreen", 1); + } catch (PendingIntent.CanceledException e) { + // ignore } } @@ -607,12 +598,4 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMainThreadHandler.post(mShadeController::collapsePanel); } } - - private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) { - if (mPresenter.isDeviceInVrMode()) { - return true; - } - - return entry.shouldSuppressFullScreenIntent(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt index 81edff45c505..1f0b96a58da6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -96,19 +96,11 @@ class StatusBarNotificationActivityStarterLogger @Inject constructor( }) } - fun logFullScreenIntentSuppressedByDnD(entry: NotificationEntry) { + fun logFullScreenIntentSuppressedByVR(entry: NotificationEntry) { buffer.log(TAG, DEBUG, { str1 = entry.logKey }, { - "No Fullscreen intent: suppressed by DND: $str1" - }) - } - - fun logFullScreenIntentNotImportantEnough(entry: NotificationEntry) { - buffer.log(TAG, DEBUG, { - str1 = entry.logKey - }, { - "No Fullscreen intent: not important enough: $str1" + "No Fullscreen intent: suppressed by VR mode: $str1" }) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 3ff7639e9262..422720e4a1da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga 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.provider.LaunchFullScreenIntentProvider 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 @@ -86,6 +87,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val mRemoteInputManager: NotificationRemoteInputManager = mock() private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock() private val mHeaderController: NodeController = mock() + private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider = mock() private lateinit var mEntry: NotificationEntry private lateinit var mGroupSummary: NotificationEntry @@ -110,6 +112,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { mHeadsUpViewBinder, mNotificationInterruptStateProvider, mRemoteInputManager, + mLaunchFullScreenIntentProvider, mHeaderController, mExecutor) mCoordinator.attach(mNotifPipeline) @@ -242,6 +245,20 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { } @Test + fun testOnEntryAdded_shouldFullScreen() { + setShouldFullScreen(mEntry) + mCollectionListener.onEntryAdded(mEntry) + verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry) + } + + @Test + fun testOnEntryAdded_shouldNotFullScreen() { + setShouldFullScreen(mEntry, should = false) + mCollectionListener.onEntryAdded(mEntry) + verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any()) + } + + @Test fun testPromotesAddedHUN() { // GIVEN the current entry should heads up whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true) @@ -755,6 +772,11 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { .thenReturn(should) } + private fun setShouldFullScreen(entry: NotificationEntry, should: Boolean = true) { + whenever(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .thenReturn(should) + } + private fun finishBind(entry: NotificationEntry) { verify(mHeadsUpManager, never()).showNotification(entry) withArgCaptor<BindCallback> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index c4098578197b..ce54d784520c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -67,8 +67,8 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -123,8 +123,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private ShadeControllerImpl mShadeController; @Mock - private NotifPipeline mNotifPipeline; - @Mock private NotificationVisibilityProvider mVisibilityProvider; @Mock private ActivityIntentHelper mActivityIntentHelper; @@ -197,7 +195,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { getContext(), mHandler, mUiBgExecutor, - mNotifPipeline, mVisibilityProvider, headsUpManager, mActivityStarter, @@ -222,7 +219,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(NotificationPresenter.class), mock(NotificationPanelViewController.class), mActivityLaunchAnimator, - notificationAnimationProvider + notificationAnimationProvider, + mock(LaunchFullScreenIntentProvider.class) ); // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg @@ -384,11 +382,9 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { NotificationEntry entry = mock(NotificationEntry.class); when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH); when(entry.getSbn()).thenReturn(sbn); - when(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(eq(entry))) - .thenReturn(true); // WHEN - mNotificationActivityStarter.handleFullScreenIntent(entry); + mNotificationActivityStarter.launchFullScreenIntent(entry); // THEN display should try wake up for the full screen intent verify(mCentralSurfaces).wakeUpForFullScreenIntent(); |