Merge "Extend lifetime of notifs during activity launch" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-04 20:43:23 +00:00
committed by Android (Google) Code Review
19 changed files with 580 additions and 204 deletions

View File

@@ -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
}
}
}
}

View File

@@ -58,7 +58,8 @@ class NotifCoordinatorsImpl @Inject constructor(
smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
viewConfigCoordinator: ViewConfigCoordinator, viewConfigCoordinator: ViewConfigCoordinator,
visualStabilityCoordinator: VisualStabilityCoordinator, visualStabilityCoordinator: VisualStabilityCoordinator,
sensitiveContentCoordinator: SensitiveContentCoordinator sensitiveContentCoordinator: SensitiveContentCoordinator,
activityLaunchAnimCoordinator: ActivityLaunchAnimCoordinator
) : NotifCoordinators { ) : NotifCoordinators {
private val mCoordinators: MutableList<Coordinator> = ArrayList() private val mCoordinators: MutableList<Coordinator> = ArrayList()
@@ -97,6 +98,7 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(viewConfigCoordinator) mCoordinators.add(viewConfigCoordinator)
mCoordinators.add(visualStabilityCoordinator) mCoordinators.add(visualStabilityCoordinator)
mCoordinators.add(sensitiveContentCoordinator) mCoordinators.add(sensitiveContentCoordinator)
mCoordinators.add(activityLaunchAnimCoordinator)
if (notifPipelineFlags.isSmartspaceDedupingEnabled()) { if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator) mCoordinators.add(smartspaceDedupingCoordinator)
} }
@@ -144,4 +146,4 @@ class NotifCoordinatorsImpl @Inject constructor(
companion object { companion object {
private const val TAG = "NotifCoordinators" private const val TAG = "NotifCoordinators"
} }
} }

View File

@@ -34,11 +34,11 @@ import dagger.Binds
import dagger.Module import dagger.Module
import javax.inject.Inject import javax.inject.Inject
@Module(includes = [PrivateModule::class]) @Module(includes = [PrivateSensitiveContentCoordinatorModule::class])
interface SensitiveContentCoordinatorModule interface SensitiveContentCoordinatorModule
@Module @Module
private interface PrivateModule { private interface PrivateSensitiveContentCoordinatorModule {
@Binds @Binds
fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator
} }
@@ -121,4 +121,4 @@ private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<Noti
if (listEntry is GroupEntry) { if (listEntry is GroupEntry) {
yieldAll(extractAllRepresentativeEntries(listEntry.children)) yieldAll(extractAllRepresentativeEntries(listEntry.children))
} }
} }

View File

@@ -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.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; 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.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.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -53,10 +53,10 @@ import javax.inject.Inject;
// TODO(b/204468557): Move to @CoordinatorScope // TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton @SysUISingleton
public class VisualStabilityCoordinator implements Coordinator, Dumpable, public class VisualStabilityCoordinator implements Coordinator, Dumpable,
NotifPanelEventSource.Callbacks { NotifPanelEvents.Listener {
private final DelayableExecutor mDelayableExecutor; private final DelayableExecutor mDelayableExecutor;
private final HeadsUpManager mHeadsUpManager; private final HeadsUpManager mHeadsUpManager;
private final NotifPanelEventSource mNotifPanelEventSource; private final NotifPanelEvents mNotifPanelEvents;
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;
private final VisualStabilityProvider mVisualStabilityProvider; private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle; private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -87,7 +87,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
DelayableExecutor delayableExecutor, DelayableExecutor delayableExecutor,
DumpManager dumpManager, DumpManager dumpManager,
HeadsUpManager headsUpManager, HeadsUpManager headsUpManager,
NotifPanelEventSource notifPanelEventSource, NotifPanelEvents notifPanelEvents,
StatusBarStateController statusBarStateController, StatusBarStateController statusBarStateController,
VisualStabilityProvider visualStabilityProvider, VisualStabilityProvider visualStabilityProvider,
WakefulnessLifecycle wakefulnessLifecycle) { WakefulnessLifecycle wakefulnessLifecycle) {
@@ -96,7 +96,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
mWakefulnessLifecycle = wakefulnessLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor; mDelayableExecutor = delayableExecutor;
mNotifPanelEventSource = notifPanelEventSource; mNotifPanelEvents = notifPanelEvents;
dumpManager.registerDumpable(this); dumpManager.registerDumpable(this);
} }
@@ -109,7 +109,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
mStatusBarStateController.addCallback(mStatusBarStateControllerListener); mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
mPulsing = mStatusBarStateController.isPulsing(); mPulsing = mStatusBarStateController.isPulsing();
mNotifPanelEventSource.registerCallbacks(this); mNotifPanelEvents.registerListener(this);
pipeline.setVisualStabilityManager(mNotifStabilityManager); pipeline.setVisualStabilityManager(mNotifStabilityManager);
} }

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator.dagger package com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.dagger.SysUISingleton 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.NotifCoordinators
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinatorsImpl import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinatorsImpl
import com.android.systemui.statusbar.notification.collection.coordinator.SensitiveContentCoordinatorModule 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 { private abstract class InternalCoordinatorsModule {
@Binds @Binds
@Internal @Internal
@@ -62,4 +66,4 @@ private annotation class Internal
@Scope @Scope
@MustBeDocumented @MustBeDocumented
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class CoordinatorScope annotation class CoordinatorScope

View File

@@ -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
}
}

View File

@@ -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.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl; 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.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.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController; 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.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.CentralSurfaces; 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.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesManager;
@@ -106,8 +107,9 @@ import dagger.Provides;
*/ */
@Module(includes = { @Module(includes = {
CoordinatorsModule.class, CoordinatorsModule.class,
NotifActivityLaunchEventsModule.class,
NotifPipelineChoreographerModule.class, NotifPipelineChoreographerModule.class,
NotifPanelEventSourceModule.class, NotifPanelEventsModule.class,
NotificationSectionHeadersModule.class, NotificationSectionHeadersModule.class,
}) })
public interface NotificationsModule { public interface NotificationsModule {

View File

@@ -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)
}
}

View File

@@ -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);
}

View File

@@ -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)
}
}

View File

@@ -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);
}

View File

@@ -128,6 +128,7 @@ import com.android.systemui.communal.CommunalSourceMonitor;
import com.android.systemui.communal.CommunalStateController; import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.communal.dagger.CommunalViewComponent; import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent; 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.DisplayId;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog; 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.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; 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.collection.render.ShadeViewManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -223,8 +223,7 @@ import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
@CentralSurfacesComponent.CentralSurfacesScope @CentralSurfacesComponent.CentralSurfacesScope
public class NotificationPanelViewController extends PanelViewController public class NotificationPanelViewController extends PanelViewController {
implements NotifPanelEventSource {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -335,6 +334,7 @@ public class NotificationPanelViewController extends PanelViewController
private final TapAgainViewController mTapAgainViewController; private final TapAgainViewController mTapAgainViewController;
private final SplitShadeHeaderController mSplitShadeHeaderController; private final SplitShadeHeaderController mSplitShadeHeaderController;
private final RecordingController mRecordingController; private final RecordingController mRecordingController;
private final PanelEventsEmitter mPanelEventsEmitter;
private boolean mShouldUseSplitNotificationShade; private boolean mShouldUseSplitNotificationShade;
// The bottom padding reserved for elements of the keyguard measuring notifications // The bottom padding reserved for elements of the keyguard measuring notifications
private float mKeyguardNotificationBottomPadding; private float mKeyguardNotificationBottomPadding;
@@ -664,8 +664,6 @@ public class NotificationPanelViewController extends PanelViewController
private Optional<NotificationPanelUnfoldAnimationController> private Optional<NotificationPanelUnfoldAnimationController>
mNotificationPanelUnfoldAnimationController; mNotificationPanelUnfoldAnimationController;
private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
private final NotificationListContainer mNotificationListContainer; private final NotificationListContainer mNotificationListContainer;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@@ -796,7 +794,8 @@ public class NotificationPanelViewController extends PanelViewController
QsFrameTranslateController qsFrameTranslateController, QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState, SysUiState sysUiState,
KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController,
NotificationListContainer notificationListContainer) { NotificationListContainer notificationListContainer,
PanelEventsEmitter panelEventsEmitter) {
super(view, super(view,
falsingManager, falsingManager,
dozeLog, dozeLog,
@@ -868,6 +867,7 @@ public class NotificationPanelViewController extends PanelViewController
mSecureSettings = secureSettings; mSecureSettings = secureSettings;
mInteractionJankMonitor = interactionJankMonitor; mInteractionJankMonitor = interactionJankMonitor;
mSysUiState = sysUiState; mSysUiState = sysUiState;
mPanelEventsEmitter = panelEventsEmitter;
pulseExpansionHandler.setPulseExpandAbortListener(() -> { pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) { if (mQs != null) {
mQs.animateHeaderSlidingOut(); mQs.animateHeaderSlidingOut();
@@ -3466,9 +3466,7 @@ public class NotificationPanelViewController extends PanelViewController
boolean wasRunning = isLaunchTransitionRunning(); boolean wasRunning = isLaunchTransitionRunning();
super.setIsLaunchAnimationRunning(running); super.setIsLaunchAnimationRunning(running);
if (wasRunning != isLaunchTransitionRunning()) { if (wasRunning != isLaunchTransitionRunning()) {
for (Callbacks cb : mNotifEventSourceCallbacks) { mPanelEventsEmitter.notifyLaunchingActivityChanged(running);
cb.onLaunchingActivityChanged(running);
}
} }
} }
@@ -3477,9 +3475,7 @@ public class NotificationPanelViewController extends PanelViewController
boolean wasClosing = isClosing(); boolean wasClosing = isClosing();
super.setIsClosing(isClosing); super.setIsClosing(isClosing);
if (wasClosing != isClosing) { if (wasClosing != isClosing) {
for (Callbacks cb : mNotifEventSourceCallbacks) { mPanelEventsEmitter.notifyPanelCollapsingChanged(isClosing);
cb.onPanelCollapsingChanged(isClosing);
}
} }
} }
@@ -4381,16 +4377,6 @@ public class NotificationPanelViewController extends PanelViewController
.commitUpdate(mDisplayId); .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 { private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override @Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) { public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -5164,4 +5150,35 @@ public class NotificationPanelViewController extends PanelViewController
1.0f /* speedUpFactor */); 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);
}
}
}
} }

View File

@@ -41,6 +41,7 @@ import android.text.TextUtils;
import android.util.EventLog; import android.util.EventLog;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor;
@@ -51,6 +52,7 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags; import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue; 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.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional; import java.util.Optional;
@@ -128,6 +131,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private final ActivityLaunchAnimator mActivityLaunchAnimator; private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
private final OnUserInteractionCallback mOnUserInteractionCallback; private final OnUserInteractionCallback mOnUserInteractionCallback;
private final LaunchEventsEmitter mLaunchEventsEmitter;
private boolean mIsCollapsingToShowActivityOverLockscreen; private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -166,7 +170,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
NotificationPresenter presenter, NotificationPresenter presenter,
NotificationPanelViewController panel, NotificationPanelViewController panel,
ActivityLaunchAnimator activityLaunchAnimator, ActivityLaunchAnimator activityLaunchAnimator,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) { NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
LaunchEventsEmitter launchEventsEmitter) {
mContext = context; mContext = context;
mCommandQueue = commandQueue; mCommandQueue = commandQueue;
mMainThreadHandler = mainThreadHandler; mMainThreadHandler = mainThreadHandler;
@@ -192,18 +197,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mLockPatternUtils = lockPatternUtils; mLockPatternUtils = lockPatternUtils;
mStatusBarRemoteInputCallback = remoteInputCallback; mStatusBarRemoteInputCallback = remoteInputCallback;
mActivityIntentHelper = activityIntentHelper; mActivityIntentHelper = activityIntentHelper;
mNotifPipelineFlags = notifPipelineFlags; mNotifPipelineFlags = notifPipelineFlags;
mMetricsLogger = metricsLogger; mMetricsLogger = metricsLogger;
mLogger = logger; mLogger = logger;
mOnUserInteractionCallback = onUserInteractionCallback; mOnUserInteractionCallback = onUserInteractionCallback;
// TODO: use KeyguardStateController#isOccluded to remove this dependency // TODO: use KeyguardStateController#isOccluded to remove this dependency
mCentralSurfaces = centralSurfaces; mCentralSurfaces = centralSurfaces;
mPresenter = presenter; mPresenter = presenter;
mNotificationPanel = panel; mNotificationPanel = panel;
mActivityLaunchAnimator = activityLaunchAnimator; mActivityLaunchAnimator = activityLaunchAnimator;
mNotificationAnimationProvider = notificationAnimationProvider; mNotificationAnimationProvider = notificationAnimationProvider;
mLaunchEventsEmitter = launchEventsEmitter;
if (!mNotifPipelineFlags.isNewPipelineEnabled()) { if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@@ -254,6 +258,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
return; return;
} }
mLaunchEventsEmitter.notifyStartLaunchNotifActivity(entry);
boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble; boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
final boolean willLaunchResolverActivity = isActivityIntent final boolean willLaunchResolverActivity = isActivityIntent
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
@@ -280,7 +286,9 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
postKeyguardAction.onDismiss(); postKeyguardAction.onDismiss();
} else { } else {
mActivityStarter.dismissKeyguardThenExecute( 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 // inform NMS that the notification was clicked
mClickNotifier.onNotificationClick(notificationKey, nv); mClickNotifier.onNotificationClick(notificationKey, nv);
if (!canBubble) { if (!canBubble && (shouldAutoCancel
if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory( || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey))) {
notificationKey)) { // Immediately remove notification from visually showing.
// Immediately remove notification from visually showing. // We have to post the removal to the UI thread for synchronization.
// We have to post the removal to the UI thread for synchronization. mMainThreadHandler.post(() -> {
mMainThreadHandler.post(() -> { final Runnable removeNotification = () -> {
final Runnable removeNotification = () -> mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK, summaryToRemove);
mOnUserInteractionCallback.onDismiss( mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry);
entry, REASON_CLICK, summaryToRemove); };
if (mPresenter.isCollapsing()) { if (mPresenter.isCollapsing()) {
// To avoid lags we're only performing the remove // To avoid lags we're only performing the remove
// after the shade is collapsed // after the shade is collapsed
mShadeController.addPostCollapseAction(removeNotification); mShadeController.addPostCollapseAction(removeNotification);
} else { } else {
removeNotification.run(); removeNotification.run();
} }
}); });
} } else {
mMainThreadHandler.post(
() -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry));
} }
mIsCollapsingToShowActivityOverLockscreen = false; mIsCollapsingToShowActivityOverLockscreen = false;
@@ -659,4 +669,35 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
return entry.shouldSuppressFullScreenIntent(); 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);
}
}
}
} }

View File

@@ -27,7 +27,6 @@ import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.core.StatusBarInitializer;
import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; 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.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule; 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? * outside the component. Should more items be moved *into* this component to avoid so many getters?
*/ */
@Subcomponent(modules = { @Subcomponent(modules = {
CentralSurfacesStartableModule.class,
NotificationStackScrollLayoutListContainerModule.class, NotificationStackScrollLayoutListContainerModule.class,
StatusBarNotifPanelEventSourceModule.class,
StatusBarViewModule.class, StatusBarViewModule.class,
StatusBarNotificationActivityStarterModule.class, StatusBarNotificationActivityStarterModule.class,
StatusBarNotificationPresenterModule.class, StatusBarNotificationPresenterModule.class,

View File

@@ -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();
}

View File

@@ -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
}
}

View File

@@ -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.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; 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.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.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.FakeSystemClock;
@@ -67,12 +67,12 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController; @Mock private StatusBarStateController mStatusBarStateController;
@Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener; @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
@Mock private HeadsUpManager mHeadsUpManager; @Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotifPanelEventSource mNotifPanelEventSource; @Mock private NotifPanelEvents mNotifPanelEvents;
@Mock private VisualStabilityProvider mVisualStabilityProvider; @Mock private VisualStabilityProvider mVisualStabilityProvider;
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor; @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
@Captor private ArgumentCaptor<NotifPanelEventSource.Callbacks> mNotifPanelEventsCallbackCaptor; @Captor private ArgumentCaptor<NotifPanelEvents.Listener> mNotifPanelEventsCallbackCaptor;
@Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor; @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -80,7 +80,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
private WakefulnessLifecycle.Observer mWakefulnessObserver; private WakefulnessLifecycle.Observer mWakefulnessObserver;
private StatusBarStateController.StateListener mStatusBarStateListener; private StatusBarStateController.StateListener mStatusBarStateListener;
private NotifPanelEventSource.Callbacks mNotifPanelEventsCallback; private NotifPanelEvents.Listener mNotifPanelEventsCallback;
private NotifStabilityManager mNotifStabilityManager; private NotifStabilityManager mNotifStabilityManager;
private NotificationEntry mEntry; private NotificationEntry mEntry;
@@ -92,7 +92,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mFakeExecutor, mFakeExecutor,
mDumpManager, mDumpManager,
mHeadsUpManager, mHeadsUpManager,
mNotifPanelEventSource, mNotifPanelEvents,
mStatusBarStateController, mStatusBarStateController,
mVisualStabilityProvider, mVisualStabilityProvider,
mWakefulnessLifecycle); mWakefulnessLifecycle);
@@ -106,7 +106,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture()); verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
mStatusBarStateListener = mSBStateListenerCaptor.getValue(); mStatusBarStateListener = mSBStateListenerCaptor.getValue();
verify(mNotifPanelEventSource).registerCallbacks(mNotifPanelEventsCallbackCaptor.capture()); verify(mNotifPanelEvents).registerListener(mNotifPanelEventsCallbackCaptor.capture());
mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue(); mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue();
verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture()); verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());

View File

@@ -361,6 +361,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private SysUiState mSysUiState; private SysUiState mSysUiState;
@Mock @Mock
private NotificationListContainer mNotificationListContainer; private NotificationListContainer mNotificationListContainer;
private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController; private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController; private NotificationPanelViewController mNotificationPanelViewController;
@@ -488,6 +489,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
mMainHandler = new Handler(Looper.getMainLooper()); mMainHandler = new Handler(Looper.getMainLooper());
mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter();
mNotificationPanelViewController = new NotificationPanelViewController(mView, mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources, mResources,
@@ -546,7 +548,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mQsFrameTranslateController, mQsFrameTranslateController,
mSysUiState, mSysUiState,
mKeyguardUnlockAnimationController, mKeyguardUnlockAnimationController,
mNotificationListContainer); mNotificationListContainer,
mPanelEventsEmitter);
mNotificationPanelViewController.initDependencies( mNotificationPanelViewController.initDependencies(
mCentralSurfaces, mCentralSurfaces,
() -> {}, () -> {},

View File

@@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationClickNotifier;
@@ -130,7 +131,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private NotifPipeline mNotifPipeline; private NotifPipeline mNotifPipeline;
@Mock @Mock
private NotificationVisibilityProvider mVisibilityProvider; private NotificationVisibilityProvider mVisibilityProvider;
@Mock @Mock
private ActivityIntentHelper mActivityIntentHelper; private ActivityIntentHelper mActivityIntentHelper;
@Mock @Mock
@@ -145,14 +145,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private ActivityLaunchAnimator mActivityLaunchAnimator; private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock @Mock
private InteractionJankMonitor mJankMonitor; private InteractionJankMonitor mJankMonitor;
private StatusBarNotificationActivityStarter.LaunchEventsEmitter mLaunchEventsEmitter;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationTestHelper mNotificationTestHelper; private NotificationTestHelper mNotificationTestHelper;
private ExpandableNotificationRow mNotificationRow; private ExpandableNotificationRow mNotificationRow;
private ExpandableNotificationRow mBubbleNotificationRow; private ExpandableNotificationRow mBubbleNotificationRow;
private final Answer<Void> mCallOnDismiss = answerVoid( private final Answer<Void> mCallOnDismiss = answerVoid(
(ActivityStarter.OnDismissAction dismissAction, Runnable cancel, (OnDismissAction dismissAction, Runnable cancel,
Boolean afterKeyguardGone) -> dismissAction.onDismiss()); Boolean afterKeyguardGone) -> dismissAction.onDismiss());
private ArrayList<NotificationEntry> mActiveNotifications; private ArrayList<NotificationEntry> mActiveNotifications;
@@ -201,7 +201,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
NotificationListContainer.class), NotificationListContainer.class),
headsUpManager, headsUpManager,
mJankMonitor); mJankMonitor);
mLaunchEventsEmitter = new StatusBarNotificationActivityStarter.LaunchEventsEmitter();
mNotificationActivityStarter = mNotificationActivityStarter =
new StatusBarNotificationActivityStarter( new StatusBarNotificationActivityStarter(
getContext(), getContext(),
@@ -237,12 +237,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(NotificationPresenter.class), mock(NotificationPresenter.class),
mock(NotificationPanelViewController.class), mock(NotificationPanelViewController.class),
mActivityLaunchAnimator, mActivityLaunchAnimator,
notificationAnimationProvider notificationAnimationProvider,
mLaunchEventsEmitter
); );
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute( 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 // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
doAnswer(answerVoid(Runnable::run)) doAnswer(answerVoid(Runnable::run))
@@ -402,4 +403,40 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// THEN display should try wake up for the full screen intent // THEN display should try wake up for the full screen intent
verify(mCentralSurfaces).wakeUpForFullScreenIntent(); 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());
}
} }