Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +7 −0 Original line number Diff line number Diff line Loading @@ -359,6 +359,13 @@ public class ShadeListBuilder implements Dumpable { private void buildList() { Trace.beginSection("ShadeListBuilder.buildList"); mPipelineState.requireIsBefore(STATE_BUILD_STARTED); if (!mNotifStabilityManager.isPipelineRunAllowed()) { mLogger.logPipelineRunSuppressed(); Trace.endSection(); return; } mPipelineState.setState(STATE_BUILD_STARTED); // Step 1: Reset notification states Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +43 −8 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.DelayableExecutor; Loading @@ -56,17 +57,23 @@ import javax.inject.Inject; */ // TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton public class VisualStabilityCoordinator implements Coordinator, Dumpable { public class VisualStabilityCoordinator implements Coordinator, Dumpable, NotifPanelEventSource.Callbacks { private final DelayableExecutor mDelayableExecutor; private final WakefulnessLifecycle mWakefulnessLifecycle; private final StatusBarStateController mStatusBarStateController; private final HeadsUpManager mHeadsUpManager; private final NotifPanelEventSource mNotifPanelEventSource; private final StatusBarStateController mStatusBarStateController; private final WakefulnessLifecycle mWakefulnessLifecycle; private boolean mScreenOn; private boolean mPanelExpanded; private boolean mPulsing; private boolean mNotifPanelCollapsing; private boolean mNotifPanelLaunchingActivity; private boolean mPipelineRunAllowed; private boolean mReorderingAllowed; private boolean mIsSuppressingPipelineRun = false; private boolean mIsSuppressingGroupChange = false; private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>(); private boolean mIsSuppressingEntryReorder = false; Loading @@ -81,16 +88,17 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { @Inject public VisualStabilityCoordinator( DelayableExecutor delayableExecutor, DumpManager dumpManager, HeadsUpManager headsUpManager, WakefulnessLifecycle wakefulnessLifecycle, NotifPanelEventSource notifPanelEventSource, StatusBarStateController statusBarStateController, DelayableExecutor delayableExecutor ) { WakefulnessLifecycle wakefulnessLifecycle) { mHeadsUpManager = headsUpManager; mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mDelayableExecutor = delayableExecutor; mNotifPanelEventSource = notifPanelEventSource; dumpManager.registerDumpable(this); } Loading @@ -103,6 +111,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { mStatusBarStateController.addCallback(mStatusBarStateControllerListener); mPulsing = mStatusBarStateController.isPulsing(); mNotifPanelEventSource.registerCallbacks(this); pipeline.setVisualStabilityManager(mNotifStabilityManager); } Loading @@ -112,11 +121,18 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { new NotifStabilityManager("VisualStabilityCoordinator") { @Override public void onBeginRun() { mIsSuppressingPipelineRun = false; mIsSuppressingGroupChange = false; mEntriesWithSuppressedSectionChange.clear(); mIsSuppressingEntryReorder = false; } @Override public boolean isPipelineRunAllowed() { mIsSuppressingPipelineRun |= !mPipelineRunAllowed; return mPipelineRunAllowed; } @Override public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) { final boolean isGroupChangeAllowedForEntry = Loading Loading @@ -154,9 +170,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { }; private void updateAllowedStates() { mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity(); mReorderingAllowed = isReorderingAllowed(); if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange() || mIsSuppressingEntryReorder)) { if ((mPipelineRunAllowed && mIsSuppressingPipelineRun) || (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange() || mIsSuppressingEntryReorder))) { mNotifStabilityManager.invalidateList(); } } Loading @@ -165,6 +184,10 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { return !mEntriesWithSuppressedSectionChange.isEmpty(); } private boolean isPanelCollapsingOrLaunchingActivity() { return mNotifPanelCollapsing || mNotifPanelLaunchingActivity; } private boolean isReorderingAllowed() { return (!mScreenOn || !mPanelExpanded) && !mPulsing; } Loading Loading @@ -248,4 +271,16 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { pw.println(" " + key); } } @Override public void onPanelCollapsingChanged(boolean isCollapsing) { mNotifPanelCollapsing = isCollapsing; updateAllowedStates(); } @Override public void onLaunchingActivityChanged(boolean isLaunchingActivity) { mNotifPanelLaunchingActivity = isLaunchingActivity; updateAllowedStates(); } } packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +3 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,9 @@ class ShadeListBuilderLogger @Inject constructor( } } } fun logPipelineRunSuppressed() = buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." } } private const val TAG = "ShadeListBuilder" No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt +11 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,16 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry */ abstract class NotifStabilityManager protected constructor(name: String) : Pluggable<NotifStabilityManager>(name) { /** * Called prior to running the pipeline to suppress any visual changes. Ex: collapse animation * is playing, moving stuff around simultaneously will look janky. * * Note: this is invoked *before* [onBeginRun], so that implementors can reference state * maintained from a previous run. */ abstract fun isPipelineRunAllowed(): Boolean /** * Called at the beginning of every pipeline run to perform any necessary cleanup from the * previous run. Loading Loading @@ -76,6 +86,7 @@ abstract class NotifStabilityManager protected constructor(name: String) : /** The default, no-op instance of the stability manager which always allows all changes */ object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabilityManager") { override fun isPipelineRunAllowed(): Boolean = true override fun onBeginRun() {} override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * 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.StatusBarComponent import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope 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 @StatusBarScope fun bindStartable( manager: NotifPanelEventSourceManager, notifPanelController: NotificationPanelViewController ): StatusBarComponent.Startable = EventSourceStatusBarStartableImpl(manager, notifPanelController) } /** * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves * come from [NotificationPanelViewController] in [StatusBarScope]. */ 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 ) : StatusBarComponent.Startable { override fun start() { manager.eventSource = notifPanelController } override fun stop() { manager.eventSource = null } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +7 −0 Original line number Diff line number Diff line Loading @@ -359,6 +359,13 @@ public class ShadeListBuilder implements Dumpable { private void buildList() { Trace.beginSection("ShadeListBuilder.buildList"); mPipelineState.requireIsBefore(STATE_BUILD_STARTED); if (!mNotifStabilityManager.isPipelineRunAllowed()) { mLogger.logPipelineRunSuppressed(); Trace.endSection(); return; } mPipelineState.setState(STATE_BUILD_STARTED); // Step 1: Reset notification states Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +43 −8 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.DelayableExecutor; Loading @@ -56,17 +57,23 @@ import javax.inject.Inject; */ // TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton public class VisualStabilityCoordinator implements Coordinator, Dumpable { public class VisualStabilityCoordinator implements Coordinator, Dumpable, NotifPanelEventSource.Callbacks { private final DelayableExecutor mDelayableExecutor; private final WakefulnessLifecycle mWakefulnessLifecycle; private final StatusBarStateController mStatusBarStateController; private final HeadsUpManager mHeadsUpManager; private final NotifPanelEventSource mNotifPanelEventSource; private final StatusBarStateController mStatusBarStateController; private final WakefulnessLifecycle mWakefulnessLifecycle; private boolean mScreenOn; private boolean mPanelExpanded; private boolean mPulsing; private boolean mNotifPanelCollapsing; private boolean mNotifPanelLaunchingActivity; private boolean mPipelineRunAllowed; private boolean mReorderingAllowed; private boolean mIsSuppressingPipelineRun = false; private boolean mIsSuppressingGroupChange = false; private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>(); private boolean mIsSuppressingEntryReorder = false; Loading @@ -81,16 +88,17 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { @Inject public VisualStabilityCoordinator( DelayableExecutor delayableExecutor, DumpManager dumpManager, HeadsUpManager headsUpManager, WakefulnessLifecycle wakefulnessLifecycle, NotifPanelEventSource notifPanelEventSource, StatusBarStateController statusBarStateController, DelayableExecutor delayableExecutor ) { WakefulnessLifecycle wakefulnessLifecycle) { mHeadsUpManager = headsUpManager; mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mDelayableExecutor = delayableExecutor; mNotifPanelEventSource = notifPanelEventSource; dumpManager.registerDumpable(this); } Loading @@ -103,6 +111,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { mStatusBarStateController.addCallback(mStatusBarStateControllerListener); mPulsing = mStatusBarStateController.isPulsing(); mNotifPanelEventSource.registerCallbacks(this); pipeline.setVisualStabilityManager(mNotifStabilityManager); } Loading @@ -112,11 +121,18 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { new NotifStabilityManager("VisualStabilityCoordinator") { @Override public void onBeginRun() { mIsSuppressingPipelineRun = false; mIsSuppressingGroupChange = false; mEntriesWithSuppressedSectionChange.clear(); mIsSuppressingEntryReorder = false; } @Override public boolean isPipelineRunAllowed() { mIsSuppressingPipelineRun |= !mPipelineRunAllowed; return mPipelineRunAllowed; } @Override public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) { final boolean isGroupChangeAllowedForEntry = Loading Loading @@ -154,9 +170,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { }; private void updateAllowedStates() { mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity(); mReorderingAllowed = isReorderingAllowed(); if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange() || mIsSuppressingEntryReorder)) { if ((mPipelineRunAllowed && mIsSuppressingPipelineRun) || (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange() || mIsSuppressingEntryReorder))) { mNotifStabilityManager.invalidateList(); } } Loading @@ -165,6 +184,10 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { return !mEntriesWithSuppressedSectionChange.isEmpty(); } private boolean isPanelCollapsingOrLaunchingActivity() { return mNotifPanelCollapsing || mNotifPanelLaunchingActivity; } private boolean isReorderingAllowed() { return (!mScreenOn || !mPanelExpanded) && !mPulsing; } Loading Loading @@ -248,4 +271,16 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { pw.println(" " + key); } } @Override public void onPanelCollapsingChanged(boolean isCollapsing) { mNotifPanelCollapsing = isCollapsing; updateAllowedStates(); } @Override public void onLaunchingActivityChanged(boolean isLaunchingActivity) { mNotifPanelLaunchingActivity = isLaunchingActivity; updateAllowedStates(); } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +3 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,9 @@ class ShadeListBuilderLogger @Inject constructor( } } } fun logPipelineRunSuppressed() = buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." } } private const val TAG = "ShadeListBuilder" No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt +11 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,16 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry */ abstract class NotifStabilityManager protected constructor(name: String) : Pluggable<NotifStabilityManager>(name) { /** * Called prior to running the pipeline to suppress any visual changes. Ex: collapse animation * is playing, moving stuff around simultaneously will look janky. * * Note: this is invoked *before* [onBeginRun], so that implementors can reference state * maintained from a previous run. */ abstract fun isPipelineRunAllowed(): Boolean /** * Called at the beginning of every pipeline run to perform any necessary cleanup from the * previous run. Loading Loading @@ -76,6 +86,7 @@ abstract class NotifStabilityManager protected constructor(name: String) : /** The default, no-op instance of the stability manager which always allows all changes */ object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabilityManager") { override fun isPipelineRunAllowed(): Boolean = true override fun onBeginRun() {} override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * 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.StatusBarComponent import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope 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 @StatusBarScope fun bindStartable( manager: NotifPanelEventSourceManager, notifPanelController: NotificationPanelViewController ): StatusBarComponent.Startable = EventSourceStatusBarStartableImpl(manager, notifPanelController) } /** * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves * come from [NotificationPanelViewController] in [StatusBarScope]. */ 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 ) : StatusBarComponent.Startable { override fun start() { manager.eventSource = notifPanelController } override fun stop() { manager.eventSource = null } }