diff options
19 files changed, 579 insertions, 203 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinator.kt new file mode 100644 index 000000000000..b54163d29e80 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinator.kt @@ -0,0 +1,95 @@ +/* + * 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.coordinator + +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.notifcollection.NotifLifetimeExtender +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback +import com.android.systemui.statusbar.phone.NotifActivityLaunchEvents +import dagger.Binds +import dagger.Module +import javax.inject.Inject + +/** Extends the lifetime of notifications while their activity launch animation is playing. */ +interface ActivityLaunchAnimCoordinator : Coordinator + +/** Provides an [ActivityLaunchAnimCoordinator] to [CoordinatorScope]. */ +@Module(includes = [PrivateActivityStarterCoordinatorModule::class]) +object ActivityLaunchAnimCoordinatorModule + +@Module +private interface PrivateActivityStarterCoordinatorModule { + @Binds + fun bindCoordinator(impl: ActivityLaunchAnimCoordinatorImpl): ActivityLaunchAnimCoordinator +} + +/** + * Listens for [NotifActivityLaunchEvents], and then extends the lifetimes of any notifs while their + * launch animation is playing. + */ +@CoordinatorScope +private class ActivityLaunchAnimCoordinatorImpl @Inject constructor( + private val activityLaunchEvents: NotifActivityLaunchEvents +) : ActivityLaunchAnimCoordinator { + // Tracks notification launches, and whether or not their lifetimes are extended. + private val notifsLaunchingActivities = mutableMapOf<String, Boolean>() + + private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null + + override fun attach(pipeline: NotifPipeline) { + activityLaunchEvents.registerListener(activityStartEventListener) + pipeline.addNotificationLifetimeExtender(extender) + } + + private val activityStartEventListener = object : NotifActivityLaunchEvents.Listener { + override fun onStartLaunchNotifActivity(entry: NotificationEntry) { + notifsLaunchingActivities[entry.key] = false + } + + override fun onFinishLaunchNotifActivity(entry: NotificationEntry) { + if (notifsLaunchingActivities.remove(entry.key) == true) { + // If we were extending the lifetime of this notification, stop. + onEndLifetimeExtensionCallback?.onEndLifetimeExtension(extender, entry) + } + } + } + + private val extender = object : NotifLifetimeExtender { + override fun getName(): String = "ActivityStarterCoordinator" + + override fun setCallback(callback: OnEndLifetimeExtensionCallback) { + onEndLifetimeExtensionCallback = callback + } + + override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { + if (entry.key in notifsLaunchingActivities) { + // Track that we're now extending this notif + notifsLaunchingActivities[entry.key] = true + return true + } + return false + } + + override fun cancelLifetimeExtension(entry: NotificationEntry) { + if (entry.key in notifsLaunchingActivities) { + notifsLaunchingActivities[entry.key] = false + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 757fb5a2fe9a..be20df460bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -58,7 +58,8 @@ class NotifCoordinatorsImpl @Inject constructor( smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, viewConfigCoordinator: ViewConfigCoordinator, visualStabilityCoordinator: VisualStabilityCoordinator, - sensitiveContentCoordinator: SensitiveContentCoordinator + sensitiveContentCoordinator: SensitiveContentCoordinator, + activityLaunchAnimCoordinator: ActivityLaunchAnimCoordinator ) : NotifCoordinators { private val mCoordinators: MutableList<Coordinator> = ArrayList() @@ -97,6 +98,7 @@ class NotifCoordinatorsImpl @Inject constructor( mCoordinators.add(viewConfigCoordinator) mCoordinators.add(visualStabilityCoordinator) mCoordinators.add(sensitiveContentCoordinator) + mCoordinators.add(activityLaunchAnimCoordinator) if (notifPipelineFlags.isSmartspaceDedupingEnabled()) { mCoordinators.add(smartspaceDedupingCoordinator) } @@ -144,4 +146,4 @@ class NotifCoordinatorsImpl @Inject constructor( companion object { private const val TAG = "NotifCoordinators" } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index 72d091883ce6..3f8a39f62dfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -34,11 +34,11 @@ import dagger.Binds import dagger.Module import javax.inject.Inject -@Module(includes = [PrivateModule::class]) +@Module(includes = [PrivateSensitiveContentCoordinatorModule::class]) interface SensitiveContentCoordinatorModule @Module -private interface PrivateModule { +private interface PrivateSensitiveContentCoordinatorModule { @Binds fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator } @@ -121,4 +121,4 @@ private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<Noti if (listEntry is GroupEntry) { yieldAll(extractAllRepresentativeEntries(listEntry.children)) } - }
\ No newline at end of file + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 3aa3549a7c27..699c4e77321e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -32,7 +32,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; -import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource; +import com.android.systemui.statusbar.phone.NotifPanelEvents; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -53,10 +53,10 @@ import javax.inject.Inject; // TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton public class VisualStabilityCoordinator implements Coordinator, Dumpable, - NotifPanelEventSource.Callbacks { + NotifPanelEvents.Listener { private final DelayableExecutor mDelayableExecutor; private final HeadsUpManager mHeadsUpManager; - private final NotifPanelEventSource mNotifPanelEventSource; + private final NotifPanelEvents mNotifPanelEvents; private final StatusBarStateController mStatusBarStateController; private final VisualStabilityProvider mVisualStabilityProvider; private final WakefulnessLifecycle mWakefulnessLifecycle; @@ -87,7 +87,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, DelayableExecutor delayableExecutor, DumpManager dumpManager, HeadsUpManager headsUpManager, - NotifPanelEventSource notifPanelEventSource, + NotifPanelEvents notifPanelEvents, StatusBarStateController statusBarStateController, VisualStabilityProvider visualStabilityProvider, WakefulnessLifecycle wakefulnessLifecycle) { @@ -96,7 +96,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mDelayableExecutor = delayableExecutor; - mNotifPanelEventSource = notifPanelEventSource; + mNotifPanelEvents = notifPanelEvents; dumpManager.registerDumpable(this); } @@ -109,7 +109,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, mStatusBarStateController.addCallback(mStatusBarStateControllerListener); mPulsing = mStatusBarStateController.isPulsing(); - mNotifPanelEventSource.registerCallbacks(this); + mNotifPanelEvents.registerListener(this); pipeline.setVisualStabilityManager(mNotifStabilityManager); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt index a26d50d2a059..274affd9da43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.coordinator.ActivityLaunchAnimCoordinatorModule import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinatorsImpl import com.android.systemui.statusbar.notification.collection.coordinator.SensitiveContentCoordinatorModule @@ -47,7 +48,10 @@ interface CoordinatorsSubcomponent { } } -@Module(includes = [SensitiveContentCoordinatorModule::class]) +@Module(includes = [ + ActivityLaunchAnimCoordinatorModule::class, + SensitiveContentCoordinatorModule::class, +]) private abstract class InternalCoordinatorsModule { @Binds @Internal @@ -62,4 +66,4 @@ private annotation class Internal @Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) -annotation class CoordinatorScope
\ No newline at end of file +annotation class CoordinatorScope diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt deleted file mode 100644 index 470737e3b772..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.render - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.phone.NotificationPanelViewController -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope -import com.android.systemui.util.ListenerSet -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.multibindings.IntoSet - -/** Provides certain notification panel events. */ -interface NotifPanelEventSource { - - /** Registers callbacks to be invoked when notification panel events occur. */ - fun registerCallbacks(callbacks: Callbacks) - - /** Unregisters callbacks previously registered via [.registerCallbacks] */ - fun unregisterCallbacks(callbacks: Callbacks) - - /** Callbacks for certain notification panel events. */ - interface Callbacks { - - /** Invoked when the notification panel starts or stops collapsing. */ - fun onPanelCollapsingChanged(isCollapsing: Boolean) - - /** - * Invoked when the notification panel starts or stops launching an [android.app.Activity]. - */ - fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) - } -} - -@Module -abstract class NotifPanelEventSourceModule { - - @Binds - @SysUISingleton - abstract fun bindEventSource(manager: NotifPanelEventSourceManager): NotifPanelEventSource - - @Module - companion object { - @JvmStatic - @Provides - fun provideManager(): NotifPanelEventSourceManager = NotifPanelEventSourceManagerImpl() - } -} - -@Module -object StatusBarNotifPanelEventSourceModule { - @JvmStatic - @Provides - @IntoSet - @CentralSurfacesScope - fun bindStartable( - manager: NotifPanelEventSourceManager, - notifPanelController: NotificationPanelViewController - ): CentralSurfacesComponent.Startable = - EventSourceStatusBarStartableImpl(manager, notifPanelController) -} - -/** - * Management layer that bridges [SysUiSingleton] and [CentralSurfacesScope]. Necessary because code - * that wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events - * themselves come from [NotificationPanelViewController] in [CentralSurfacesScope]. - */ -interface NotifPanelEventSourceManager : NotifPanelEventSource { - var eventSource: NotifPanelEventSource? -} - -private class NotifPanelEventSourceManagerImpl - : NotifPanelEventSourceManager, NotifPanelEventSource.Callbacks { - - private val callbackSet = ListenerSet<NotifPanelEventSource.Callbacks>() - - override var eventSource: NotifPanelEventSource? = null - set(value) { - field?.unregisterCallbacks(this) - value?.registerCallbacks(this) - field = value - } - - override fun registerCallbacks(callbacks: NotifPanelEventSource.Callbacks) { - callbackSet.addIfAbsent(callbacks) - } - - override fun unregisterCallbacks(callbacks: NotifPanelEventSource.Callbacks) { - callbackSet.remove(callbacks) - } - - override fun onPanelCollapsingChanged(isCollapsing: Boolean) { - callbackSet.forEach { it.onPanelCollapsingChanged(isCollapsing) } - } - - override fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) { - callbackSet.forEach { it.onLaunchingActivityChanged(isLaunchingActivity) } - } -} - -private class EventSourceStatusBarStartableImpl( - private val manager: NotifPanelEventSourceManager, - private val notifPanelController: NotificationPanelViewController -) : CentralSurfacesComponent.Startable { - - override fun start() { - manager.eventSource = notifPanelController - } - - override fun stop() { - manager.eventSource = null - } -} 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 51bbf1c80478..efe88e6d4f0a 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 @@ -68,7 +68,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans 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.collection.render.NotifPanelEventSourceModule; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -84,9 +83,11 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; +import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.NotifActivityLaunchEventsModule; +import com.android.systemui.statusbar.phone.NotifPanelEventsModule; import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.wmshell.BubblesManager; @@ -106,8 +107,9 @@ import dagger.Provides; */ @Module(includes = { CoordinatorsModule.class, + NotifActivityLaunchEventsModule.class, NotifPipelineChoreographerModule.class, - NotifPanelEventSourceModule.class, + NotifPanelEventsModule.class, NotificationSectionHeadersModule.class, }) public interface NotificationsModule { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEvents.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEvents.kt new file mode 100644 index 000000000000..f46d07338206 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEvents.kt @@ -0,0 +1,39 @@ +/* + * 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.phone + +import com.android.systemui.statusbar.notification.collection.NotificationEntry + +/** Provides events about [android.app.Activity] launches from Notifications. */ +interface NotifActivityLaunchEvents { + + /** Registers a [Listener] to be invoked when notification activity launch events occur. */ + fun registerListener(listener: Listener) + + /** Unregisters a [Listener] previously registered via [registerListener] */ + fun unregisterListener(listener: Listener) + + /** Listener for events about [android.app.Activity] launches from Notifications. */ + interface Listener { + + /** Invoked when an activity has started launching from a notification. */ + fun onStartLaunchNotifActivity(entry: NotificationEntry) + + /** Invoked when an activity has finished launching. */ + fun onFinishLaunchNotifActivity(entry: NotificationEntry) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEventsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEventsModule.java new file mode 100644 index 000000000000..84ff538677b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifActivityLaunchEventsModule.java @@ -0,0 +1,30 @@ +/* + * 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.phone; + +import com.android.systemui.dagger.SysUISingleton; + +import dagger.Binds; +import dagger.Module; + +/** Provides a {@link NotifActivityLaunchEvents} in {@link SysUISingleton} scope. */ +@Module +public abstract class NotifActivityLaunchEventsModule { + @Binds + abstract NotifActivityLaunchEvents bindLaunchEvents( + StatusBarNotificationActivityStarter.LaunchEventsEmitter impl); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt new file mode 100644 index 000000000000..a385e22c1aff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt @@ -0,0 +1,39 @@ +/* + * 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.phone + +/** Provides certain notification panel events. */ +interface NotifPanelEvents { + + /** Registers callbacks to be invoked when notification panel events occur. */ + fun registerListener(listener: Listener) + + /** Unregisters callbacks previously registered via [registerListener] */ + fun unregisterListener(listener: Listener) + + /** Callbacks for certain notification panel events. */ + interface Listener { + + /** Invoked when the notification panel starts or stops collapsing. */ + fun onPanelCollapsingChanged(isCollapsing: Boolean) + + /** + * Invoked when the notification panel starts or stops launching an [android.app.Activity]. + */ + fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java new file mode 100644 index 000000000000..2aaf6a5f4391 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java @@ -0,0 +1,30 @@ +/* + * 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.phone; + +import com.android.systemui.dagger.SysUISingleton; + +import dagger.Binds; +import dagger.Module; + +/** Provides a {@link NotifPanelEvents} in {@link SysUISingleton} scope. */ +@Module +public abstract class NotifPanelEventsModule { + @Binds + abstract NotifPanelEvents bindPanelEvents( + NotificationPanelViewController.PanelEventsEmitter impl); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 61760fb8c78c..292932f93182 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -128,6 +128,7 @@ import com.android.systemui.communal.CommunalSourceMonitor; import com.android.systemui.communal.CommunalStateController; import com.android.systemui.communal.dagger.CommunalViewComponent; import com.android.systemui.controls.dagger.ControlsComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -176,7 +177,6 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource; import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -223,8 +223,7 @@ import javax.inject.Inject; import javax.inject.Provider; @CentralSurfacesComponent.CentralSurfacesScope -public class NotificationPanelViewController extends PanelViewController - implements NotifPanelEventSource { +public class NotificationPanelViewController extends PanelViewController { private static final boolean DEBUG = false; @@ -335,6 +334,7 @@ public class NotificationPanelViewController extends PanelViewController private final TapAgainViewController mTapAgainViewController; private final SplitShadeHeaderController mSplitShadeHeaderController; private final RecordingController mRecordingController; + private final PanelEventsEmitter mPanelEventsEmitter; private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; @@ -664,8 +664,6 @@ public class NotificationPanelViewController extends PanelViewController private Optional<NotificationPanelUnfoldAnimationController> mNotificationPanelUnfoldAnimationController; - private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>(); - private final NotificationListContainer mNotificationListContainer; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @@ -796,7 +794,8 @@ public class NotificationPanelViewController extends PanelViewController QsFrameTranslateController qsFrameTranslateController, SysUiState sysUiState, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - NotificationListContainer notificationListContainer) { + NotificationListContainer notificationListContainer, + PanelEventsEmitter panelEventsEmitter) { super(view, falsingManager, dozeLog, @@ -868,6 +867,7 @@ public class NotificationPanelViewController extends PanelViewController mSecureSettings = secureSettings; mInteractionJankMonitor = interactionJankMonitor; mSysUiState = sysUiState; + mPanelEventsEmitter = panelEventsEmitter; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -3460,9 +3460,7 @@ public class NotificationPanelViewController extends PanelViewController boolean wasRunning = isLaunchTransitionRunning(); super.setIsLaunchAnimationRunning(running); if (wasRunning != isLaunchTransitionRunning()) { - for (Callbacks cb : mNotifEventSourceCallbacks) { - cb.onLaunchingActivityChanged(running); - } + mPanelEventsEmitter.notifyLaunchingActivityChanged(running); } } @@ -3471,9 +3469,7 @@ public class NotificationPanelViewController extends PanelViewController boolean wasClosing = isClosing(); super.setIsClosing(isClosing); if (wasClosing != isClosing) { - for (Callbacks cb : mNotifEventSourceCallbacks) { - cb.onPanelCollapsingChanged(isClosing); - } + mPanelEventsEmitter.notifyPanelCollapsingChanged(isClosing); } } @@ -4375,16 +4371,6 @@ public class NotificationPanelViewController extends PanelViewController .commitUpdate(mDisplayId); } - @Override - public void registerCallbacks(Callbacks callbacks) { - mNotifEventSourceCallbacks.addIfAbsent(callbacks); - } - - @Override - public void unregisterCallbacks(Callbacks callbacks) { - mNotifEventSourceCallbacks.remove(callbacks); - } - private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { @Override public void onHeightChanged(ExpandableView view, boolean needsAnimation) { @@ -5158,4 +5144,35 @@ public class NotificationPanelViewController extends PanelViewController 1.0f /* speedUpFactor */); } } + + @SysUISingleton + static class PanelEventsEmitter implements NotifPanelEvents { + + private final ListenerSet<Listener> mListeners = new ListenerSet<>(); + + @Inject + PanelEventsEmitter() {} + + @Override + public void registerListener(@NonNull Listener listener) { + mListeners.addIfAbsent(listener); + } + + @Override + public void unregisterListener(@NonNull Listener listener) { + mListeners.remove(listener); + } + + private void notifyLaunchingActivityChanged(boolean isLaunchingActivity) { + for (Listener cb : mListeners) { + cb.onLaunchingActivityChanged(isLaunchingActivity); + } + } + + private void notifyPanelCollapsingChanged(boolean isCollapsing) { + for (NotifPanelEvents.Listener cb : mListeners) { + cb.onPanelCollapsingChanged(isCollapsing); + } + } + } } 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 edbddbb3c3e7..108d98a129b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -41,6 +41,7 @@ import android.text.TextUtils; import android.util.EventLog; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; @@ -51,6 +52,7 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; @@ -75,6 +77,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.ListenerSet; import com.android.systemui.wmshell.BubblesManager; import java.util.Optional; @@ -128,6 +131,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; private final OnUserInteractionCallback mOnUserInteractionCallback; + private final LaunchEventsEmitter mLaunchEventsEmitter; private boolean mIsCollapsingToShowActivityOverLockscreen; @@ -166,7 +170,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte NotificationPresenter presenter, NotificationPanelViewController panel, ActivityLaunchAnimator activityLaunchAnimator, - NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) { + NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, + LaunchEventsEmitter launchEventsEmitter) { mContext = context; mCommandQueue = commandQueue; mMainThreadHandler = mainThreadHandler; @@ -192,18 +197,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLockPatternUtils = lockPatternUtils; mStatusBarRemoteInputCallback = remoteInputCallback; mActivityIntentHelper = activityIntentHelper; - mNotifPipelineFlags = notifPipelineFlags; mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; - // TODO: use KeyguardStateController#isOccluded to remove this dependency mCentralSurfaces = centralSurfaces; mPresenter = presenter; mNotificationPanel = panel; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; + mLaunchEventsEmitter = launchEventsEmitter; if (!mNotifPipelineFlags.isNewPipelineEnabled()) { mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @@ -254,6 +258,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte return; } + mLaunchEventsEmitter.notifyStartLaunchNotifActivity(entry); + boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble; final boolean willLaunchResolverActivity = isActivityIntent && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), @@ -280,7 +286,9 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte postKeyguardAction.onDismiss(); } else { mActivityStarter.dismissKeyguardThenExecute( - postKeyguardAction, null /* cancel */, willLaunchResolverActivity); + postKeyguardAction, + () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry), + willLaunchResolverActivity); } } @@ -380,24 +388,26 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte // inform NMS that the notification was clicked mClickNotifier.onNotificationClick(notificationKey, nv); - if (!canBubble) { - if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory( - notificationKey)) { - // Immediately remove notification from visually showing. - // We have to post the removal to the UI thread for synchronization. - mMainThreadHandler.post(() -> { - final Runnable removeNotification = () -> - mOnUserInteractionCallback.onDismiss( - entry, REASON_CLICK, summaryToRemove); - if (mPresenter.isCollapsing()) { - // To avoid lags we're only performing the remove - // after the shade is collapsed - mShadeController.addPostCollapseAction(removeNotification); - } else { - removeNotification.run(); - } - }); - } + if (!canBubble && (shouldAutoCancel + || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey))) { + // Immediately remove notification from visually showing. + // We have to post the removal to the UI thread for synchronization. + mMainThreadHandler.post(() -> { + final Runnable removeNotification = () -> { + mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK, summaryToRemove); + mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry); + }; + if (mPresenter.isCollapsing()) { + // To avoid lags we're only performing the remove + // after the shade is collapsed + mShadeController.addPostCollapseAction(removeNotification); + } else { + removeNotification.run(); + } + }); + } else { + mMainThreadHandler.post( + () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry)); } mIsCollapsingToShowActivityOverLockscreen = false; @@ -659,4 +669,35 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte return entry.shouldSuppressFullScreenIntent(); } + + @SysUISingleton + static class LaunchEventsEmitter implements NotifActivityLaunchEvents { + + private final ListenerSet<Listener> mListeners = new ListenerSet<>(); + + @Inject + LaunchEventsEmitter() {} + + @Override + public void registerListener(@NonNull Listener listener) { + mListeners.addIfAbsent(listener); + } + + @Override + public void unregisterListener(@NonNull Listener listener) { + mListeners.remove(listener); + } + + private void notifyStartLaunchNotifActivity(NotificationEntry entry) { + for (Listener listener : mListeners) { + listener.onStartLaunchNotifActivity(entry); + } + } + + private void notifyFinishLaunchNotifActivity(NotificationEntry entry) { + for (Listener listener : mListeners) { + listener.onFinishLaunchNotifActivity(entry); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java index 59c9d0baea3f..a86ad6bc7012 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; -import com.android.systemui.statusbar.notification.collection.render.StatusBarNotifPanelEventSourceModule; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule; @@ -60,8 +59,8 @@ import dagger.Subcomponent; * outside the component. Should more items be moved *into* this component to avoid so many getters? */ @Subcomponent(modules = { + CentralSurfacesStartableModule.class, NotificationStackScrollLayoutListContainerModule.class, - StatusBarNotifPanelEventSourceModule.class, StatusBarViewModule.class, StatusBarNotificationActivityStarterModule.class, StatusBarNotificationPresenterModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java new file mode 100644 index 000000000000..21e5ad5778b1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java @@ -0,0 +1,28 @@ +/* + * 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.phone.dagger; + +import java.util.Set; + +import dagger.Module; +import dagger.multibindings.Multibinds; + +@Module +interface CentralSurfacesStartableModule { + @Multibinds + Set<CentralSurfacesComponent.Startable> multibindStartables(); +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinatorTest.kt new file mode 100644 index 000000000000..c6c043aafb20 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ActivityLaunchAnimCoordinatorTest.kt @@ -0,0 +1,138 @@ +/* + * 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.coordinator + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +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.notifcollection.NotifLifetimeExtender +import com.android.systemui.statusbar.phone.NotifActivityLaunchEvents +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.withArgCaptor +import dagger.BindsInstance +import dagger.Component +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +class ActivityLaunchAnimCoordinatorTest : SysuiTestCase() { + + val activityLaunchEvents: NotifActivityLaunchEvents = mock() + val pipeline: NotifPipeline = mock() + + val coordinator: ActivityLaunchAnimCoordinator = + DaggerTestActivityStarterCoordinatorComponent + .factory() + .create(activityLaunchEvents) + .coordinator + + @Test + fun testNoLifetimeExtensionIfNoAssociatedActivityLaunch() { + coordinator.attach(pipeline) + val lifetimeExtender = withArgCaptor<NotifLifetimeExtender> { + verify(pipeline).addNotificationLifetimeExtender(capture()) + } + val fakeEntry = mock<NotificationEntry>().also { + whenever(it.key).thenReturn("0") + } + assertFalse(lifetimeExtender.maybeExtendLifetime(fakeEntry, 0)) + } + + @Test + fun testNoLifetimeExtensionIfAssociatedActivityLaunchAlreadyEnded() { + coordinator.attach(pipeline) + val lifetimeExtender = withArgCaptor<NotifLifetimeExtender> { + verify(pipeline).addNotificationLifetimeExtender(capture()) + } + val eventListener = withArgCaptor<NotifActivityLaunchEvents.Listener> { + verify(activityLaunchEvents).registerListener(capture()) + } + val fakeEntry = mock<NotificationEntry>().also { + whenever(it.key).thenReturn("0") + } + eventListener.onStartLaunchNotifActivity(fakeEntry) + eventListener.onFinishLaunchNotifActivity(fakeEntry) + assertFalse(lifetimeExtender.maybeExtendLifetime(fakeEntry, 0)) + } + + @Test + fun testLifetimeExtensionWhileActivityLaunchInProgress() { + coordinator.attach(pipeline) + val lifetimeExtender = withArgCaptor<NotifLifetimeExtender> { + verify(pipeline).addNotificationLifetimeExtender(capture()) + } + val eventListener = withArgCaptor<NotifActivityLaunchEvents.Listener> { + verify(activityLaunchEvents).registerListener(capture()) + } + val onEndLifetimeExtensionCallback = + mock<NotifLifetimeExtender.OnEndLifetimeExtensionCallback>() + lifetimeExtender.setCallback(onEndLifetimeExtensionCallback) + + val fakeEntry = mock<NotificationEntry>().also { + whenever(it.key).thenReturn("0") + } + eventListener.onStartLaunchNotifActivity(fakeEntry) + assertTrue(lifetimeExtender.maybeExtendLifetime(fakeEntry, 0)) + + eventListener.onFinishLaunchNotifActivity(fakeEntry) + verify(onEndLifetimeExtensionCallback).onEndLifetimeExtension(lifetimeExtender, fakeEntry) + } + + @Test + fun testCancelLifetimeExtensionDoesNotInvokeCallback() { + coordinator.attach(pipeline) + val lifetimeExtender = withArgCaptor<NotifLifetimeExtender> { + verify(pipeline).addNotificationLifetimeExtender(capture()) + } + val eventListener = withArgCaptor<NotifActivityLaunchEvents.Listener> { + verify(activityLaunchEvents).registerListener(capture()) + } + val onEndLifetimeExtensionCallback = + mock<NotifLifetimeExtender.OnEndLifetimeExtensionCallback>() + lifetimeExtender.setCallback(onEndLifetimeExtensionCallback) + + val fakeEntry = mock<NotificationEntry>().also { + whenever(it.key).thenReturn("0") + } + eventListener.onStartLaunchNotifActivity(fakeEntry) + assertTrue(lifetimeExtender.maybeExtendLifetime(fakeEntry, 0)) + + lifetimeExtender.cancelLifetimeExtension(fakeEntry) + eventListener.onFinishLaunchNotifActivity(fakeEntry) + verify(onEndLifetimeExtensionCallback, never()) + .onEndLifetimeExtension(lifetimeExtender, fakeEntry) + } +} + +@CoordinatorScope +@Component(modules = [ActivityLaunchAnimCoordinatorModule::class]) +interface TestActivityStarterCoordinatorComponent { + val coordinator: ActivityLaunchAnimCoordinator + + @Component.Factory + interface Factory { + fun create( + @BindsInstance activityLaunchEvents: NotifActivityLaunchEvents + ): TestActivityStarterCoordinatorComponent + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index ee111715e5ea..6f8e5d8e514e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -41,7 +41,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; -import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource; +import com.android.systemui.statusbar.phone.NotifPanelEvents; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -67,12 +67,12 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener; @Mock private HeadsUpManager mHeadsUpManager; - @Mock private NotifPanelEventSource mNotifPanelEventSource; + @Mock private NotifPanelEvents mNotifPanelEvents; @Mock private VisualStabilityProvider mVisualStabilityProvider; @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor; - @Captor private ArgumentCaptor<NotifPanelEventSource.Callbacks> mNotifPanelEventsCallbackCaptor; + @Captor private ArgumentCaptor<NotifPanelEvents.Listener> mNotifPanelEventsCallbackCaptor; @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor; private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); @@ -80,7 +80,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { private WakefulnessLifecycle.Observer mWakefulnessObserver; private StatusBarStateController.StateListener mStatusBarStateListener; - private NotifPanelEventSource.Callbacks mNotifPanelEventsCallback; + private NotifPanelEvents.Listener mNotifPanelEventsCallback; private NotifStabilityManager mNotifStabilityManager; private NotificationEntry mEntry; @@ -92,7 +92,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mFakeExecutor, mDumpManager, mHeadsUpManager, - mNotifPanelEventSource, + mNotifPanelEvents, mStatusBarStateController, mVisualStabilityProvider, mWakefulnessLifecycle); @@ -106,7 +106,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture()); mStatusBarStateListener = mSBStateListenerCaptor.getValue(); - verify(mNotifPanelEventSource).registerCallbacks(mNotifPanelEventsCallbackCaptor.capture()); + verify(mNotifPanelEvents).registerListener(mNotifPanelEventsCallbackCaptor.capture()); mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue(); verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 38412fd1fa9b..e58f557844b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -361,6 +361,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { private SysUiState mSysUiState; @Mock private NotificationListContainer mNotificationListContainer; + private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter; private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; @@ -488,6 +489,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); mMainHandler = new Handler(Looper.getMainLooper()); + mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter(); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, @@ -546,7 +548,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mQsFrameTranslateController, mSysUiState, mKeyguardUnlockAnimationController, - mNotificationListContainer); + mNotificationListContainer, + mPanelEventsEmitter); mNotificationPanelViewController.initDependencies( mCentralSurfaces, () -> {}, 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 ace7415f2c17..27179fd7be92 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 @@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationClickNotifier; @@ -130,7 +131,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private NotifPipeline mNotifPipeline; @Mock private NotificationVisibilityProvider mVisibilityProvider; - @Mock private ActivityIntentHelper mActivityIntentHelper; @Mock @@ -145,14 +145,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; + private StatusBarNotificationActivityStarter.LaunchEventsEmitter mLaunchEventsEmitter; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private NotificationTestHelper mNotificationTestHelper; private ExpandableNotificationRow mNotificationRow; private ExpandableNotificationRow mBubbleNotificationRow; private final Answer<Void> mCallOnDismiss = answerVoid( - (ActivityStarter.OnDismissAction dismissAction, Runnable cancel, + (OnDismissAction dismissAction, Runnable cancel, Boolean afterKeyguardGone) -> dismissAction.onDismiss()); private ArrayList<NotificationEntry> mActiveNotifications; @@ -201,7 +201,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { NotificationListContainer.class), headsUpManager, mJankMonitor); - + mLaunchEventsEmitter = new StatusBarNotificationActivityStarter.LaunchEventsEmitter(); mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), @@ -237,12 +237,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(NotificationPresenter.class), mock(NotificationPanelViewController.class), mActivityLaunchAnimator, - notificationAnimationProvider + notificationAnimationProvider, + mLaunchEventsEmitter ); // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute( - any(ActivityStarter.OnDismissAction.class), any(), anyBoolean()); + any(OnDismissAction.class), any(), anyBoolean()); // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) @@ -402,4 +403,40 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // THEN display should try wake up for the full screen intent verify(mCentralSurfaces).wakeUpForFullScreenIntent(); } + + @Test + public void testNotifActivityStarterEventSourceStartEvent_onNotificationClicked() { + NotifActivityLaunchEvents.Listener listener = + mock(NotifActivityLaunchEvents.Listener.class); + mLaunchEventsEmitter.registerListener(listener); + mNotificationActivityStarter + .onNotificationClicked(mNotificationRow.getEntry().getSbn(), mNotificationRow); + verify(listener).onStartLaunchNotifActivity(mNotificationRow.getEntry()); + } + + @Test + public void testNotifActivityStarterEventSourceFinishEvent_dismissKeyguardCancelled() { + NotifActivityLaunchEvents.Listener listener = + mock(NotifActivityLaunchEvents.Listener.class); + mLaunchEventsEmitter.registerListener(listener); + // set up dismissKeyguardThenExecute to synchronously invoke the cancel runnable arg + doAnswer(answerVoid( + (OnDismissAction dismissAction, Runnable cancel, Boolean afterKeyguardGone) -> + cancel.run())) + .when(mActivityStarter) + .dismissKeyguardThenExecute(any(OnDismissAction.class), any(), anyBoolean()); + mNotificationActivityStarter + .onNotificationClicked(mNotificationRow.getEntry().getSbn(), mNotificationRow); + verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry()); + } + + @Test + public void testNotifActivityStarterEventSourceFinishEvent_postPanelCollapse() { + NotifActivityLaunchEvents.Listener listener = + mock(NotifActivityLaunchEvents.Listener.class); + mLaunchEventsEmitter.registerListener(listener); + mNotificationActivityStarter + .onNotificationClicked(mNotificationRow.getEntry().getSbn(), mNotificationRow); + verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry()); + } } |