• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.notification.collection.coordinator;
18 
19 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.VisibleForTesting;
23 
24 import com.android.systemui.Dumpable;
25 import com.android.systemui.Flags;
26 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
27 import com.android.systemui.dagger.SysUISingleton;
28 import com.android.systemui.dagger.qualifiers.Background;
29 import com.android.systemui.dagger.qualifiers.Main;
30 import com.android.systemui.dump.DumpManager;
31 import com.android.systemui.keyguard.WakefulnessLifecycle;
32 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
33 import com.android.systemui.keyguard.shared.model.Edge;
34 import com.android.systemui.keyguard.shared.model.KeyguardState;
35 import com.android.systemui.plugins.statusbar.StatusBarStateController;
36 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
37 import com.android.systemui.scene.shared.model.Scenes;
38 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
39 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
40 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
41 import com.android.systemui.statusbar.notification.collection.GroupEntry;
42 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
43 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
44 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
45 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
46 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
47 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
48 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
49 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
50 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
51 import com.android.systemui.statusbar.policy.KeyguardStateController;
52 import com.android.systemui.util.concurrency.DelayableExecutor;
53 import com.android.systemui.util.kotlin.BooleanFlowOperators;
54 import com.android.systemui.util.kotlin.JavaAdapter;
55 
56 import java.io.PrintWriter;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Set;
62 
63 import javax.inject.Inject;
64 
65 /**
66  * Ensures that notifications are visually stable if the user is looking at the notifications.
67  * Group and section changes are re-allowed when the notification entries are no longer being
68  * viewed.
69  */
70 // TODO(b/204468557): Move to @CoordinatorScope
71 @SysUISingleton
72 public class VisualStabilityCoordinator implements Coordinator, Dumpable {
73     private final DelayableExecutor mDelayableExecutor;
74     private final DelayableExecutor mMainExecutor;
75     private final HeadsUpRepository mHeadsUpRepository;
76     private final SeenNotificationsInteractor mSeenNotificationsInteractor;
77     private final ShadeAnimationInteractor mShadeAnimationInteractor;
78     private final StatusBarStateController mStatusBarStateController;
79     private final JavaAdapter mJavaAdapter;
80     private final VisibilityLocationProvider mVisibilityLocationProvider;
81     private final VisualStabilityProvider mVisualStabilityProvider;
82     private final WakefulnessLifecycle mWakefulnessLifecycle;
83     private final CommunalSceneInteractor mCommunalSceneInteractor;
84     private final ShadeInteractor mShadeInteractor;
85     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
86     private final KeyguardStateController mKeyguardStateController;
87     private final VisualStabilityCoordinatorLogger mLogger;
88 
89     private boolean mSleepy = true;
90     private boolean mFullyDozed;
91     private boolean mPanelExpanded;
92     private boolean mPulsing;
93     private boolean mNotifPanelCollapsing;
94     private boolean mNotifPanelLaunchingActivity;
95     private boolean mCommunalShowing = false;
96     private boolean mLockscreenShowing = false;
97     private boolean mTrackingHeadsUp = false;
98     private boolean mLockscreenInGoneTransition = false;
99     private Set<String> mHeadsUpGroupKeys = new HashSet<>();
100 
101     private boolean mPipelineRunAllowed;
102     private boolean mReorderingAllowed;
103     private boolean mIsSuppressingPipelineRun = false;
104     private boolean mIsSuppressingGroupChange = false;
105     private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
106     private boolean mIsSuppressingEntryReorder = false;
107 
108     // key: notification key that can temporarily change its section
109     // value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable
110     // from the DelayableExecutor's queue
111     private Map<String, Runnable> mEntriesThatCanChangeSection = new HashMap<>();
112 
113     @VisibleForTesting
114     protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
115 
116     private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
117 
118     @Inject
VisualStabilityCoordinator( @ackground DelayableExecutor delayableExecutor, @Main DelayableExecutor mainExecutor, DumpManager dumpManager, HeadsUpRepository headsUpRepository, ShadeAnimationInteractor shadeAnimationInteractor, JavaAdapter javaAdapter, SeenNotificationsInteractor seenNotificationsInteractor, StatusBarStateController statusBarStateController, VisibilityLocationProvider visibilityLocationProvider, VisualStabilityProvider visualStabilityProvider, WakefulnessLifecycle wakefulnessLifecycle, CommunalSceneInteractor communalSceneInteractor, ShadeInteractor shadeInteractor, KeyguardTransitionInteractor keyguardTransitionInteractor, KeyguardStateController keyguardStateController, VisualStabilityCoordinatorLogger logger)119     public VisualStabilityCoordinator(
120             @Background DelayableExecutor delayableExecutor,
121             @Main DelayableExecutor mainExecutor,
122             DumpManager dumpManager,
123             HeadsUpRepository headsUpRepository,
124             ShadeAnimationInteractor shadeAnimationInteractor,
125             JavaAdapter javaAdapter,
126             SeenNotificationsInteractor seenNotificationsInteractor,
127             StatusBarStateController statusBarStateController,
128             VisibilityLocationProvider visibilityLocationProvider,
129             VisualStabilityProvider visualStabilityProvider,
130             WakefulnessLifecycle wakefulnessLifecycle,
131             CommunalSceneInteractor communalSceneInteractor,
132             ShadeInteractor shadeInteractor,
133             KeyguardTransitionInteractor keyguardTransitionInteractor,
134             KeyguardStateController keyguardStateController,
135             VisualStabilityCoordinatorLogger logger) {
136         mHeadsUpRepository = headsUpRepository;
137         mShadeAnimationInteractor = shadeAnimationInteractor;
138         mJavaAdapter = javaAdapter;
139         mSeenNotificationsInteractor = seenNotificationsInteractor;
140         mVisibilityLocationProvider = visibilityLocationProvider;
141         mVisualStabilityProvider = visualStabilityProvider;
142         mWakefulnessLifecycle = wakefulnessLifecycle;
143         mStatusBarStateController = statusBarStateController;
144         mDelayableExecutor = delayableExecutor;
145         mMainExecutor = mainExecutor;
146         mCommunalSceneInteractor = communalSceneInteractor;
147         mShadeInteractor = shadeInteractor;
148         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
149         mKeyguardStateController = keyguardStateController;
150         mLogger = logger;
151 
152         dumpManager.registerDumpable(this);
153     }
154 
155     @Override
attach(NotifPipeline pipeline)156     public void attach(NotifPipeline pipeline) {
157         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
158         mSleepy = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP;
159         mFullyDozed = mStatusBarStateController.getDozeAmount() == 1f;
160 
161         mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
162         mPulsing = mStatusBarStateController.isPulsing();
163         mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isAnyCloseAnimationRunning(),
164                 this::onShadeOrQsClosingChanged);
165         mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isLaunchingActivity(),
166                 this::onLaunchingActivityChanged);
167         mJavaAdapter.alwaysCollectFlow(
168                 BooleanFlowOperators.INSTANCE.allOf(
169                         mCommunalSceneInteractor.isIdleOnCommunal(),
170                         BooleanFlowOperators.INSTANCE.not(mShadeInteractor.isAnyFullyExpanded())
171                 ),
172                 this::onCommunalShowingChanged);
173 
174 
175         if (StabilizeHeadsUpGroup.isEnabled()) {
176             pipeline.addOnBeforeRenderListListener(mOnBeforeRenderListListener);
177         }
178 
179         if (SceneContainerFlag.isEnabled()) {
180             mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.transitionValue(
181                             KeyguardState.LOCKSCREEN),
182                     this::onLockscreenKeyguardStateTransitionValueChanged);
183             mJavaAdapter.alwaysCollectFlow(mHeadsUpRepository.isTrackingHeadsUp(),
184                     this::onTrackingHeadsUpModeChanged);
185         }
186 
187         if (mCheckLockScreenTransitionEnabled) {
188             if (SceneContainerFlag.isEnabled()) {
189                 mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
190                                 Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
191                         this::onLockscreenInGoneTransitionChanged);
192             } else {
193                 mKeyguardStateController.addCallback(mKeyguardFadeAwayAnimationCallback);
194             }
195         }
196         pipeline.setVisualStabilityManager(mNotifStabilityManager);
197     }
198 
199     /**
200      * Setter of heads up group keys.
201      */
202     @VisibleForTesting
setHeadsUpGroupKeys(Set<String> currentHeadsUpGroupKeys)203     public void setHeadsUpGroupKeys(Set<String> currentHeadsUpGroupKeys) {
204         if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
205             return;
206         }
207 
208         if (currentHeadsUpGroupKeys == null) {
209             currentHeadsUpGroupKeys = new HashSet<>();
210         }
211 
212         boolean isAnyHeadsUpGroupRemoved = false;
213         for (String headsUpKey: mHeadsUpGroupKeys) {
214             if (!currentHeadsUpGroupKeys.contains(headsUpKey)) {
215                 isAnyHeadsUpGroupRemoved = true;
216                 break;
217             }
218         }
219         mHeadsUpGroupKeys = currentHeadsUpGroupKeys;
220 
221         if (isAnyHeadsUpGroupRemoved) {
222             updateAllowedStates("headsUpGroupEntryChange",
223                     mHeadsUpGroupKeys.isEmpty(), /* async= */ true);
224         }
225     }
226 
227     final KeyguardStateController.Callback mKeyguardFadeAwayAnimationCallback =
228             new KeyguardStateController.Callback() {
229                 @Override
230                 public void onKeyguardFadingAwayChanged() {
231                     onLockscreenInGoneTransitionChanged(
232                             mKeyguardStateController.isKeyguardFadingAway());
233                 }
234             };
235 
236     // TODO(b/203826051): Ensure stability manager can allow reordering off-screen
237     //  HUNs to the top of the shade
238     private final NotifStabilityManager mNotifStabilityManager =
239             new NotifStabilityManager("VisualStabilityCoordinator") {
240                 private boolean canMoveForHeadsUp(NotificationEntry entry) {
241                     if (entry == null) {
242                         return false;
243                     }
244                     boolean isTopUnseen = NotificationMinimalism.isEnabled()
245                             && (mSeenNotificationsInteractor.isTopUnseenNotification(entry)
246                                 || mSeenNotificationsInteractor.isTopOngoingNotification(entry));
247                     if (isTopUnseen || mHeadsUpRepository.isHeadsUpEntry(entry.getKey())) {
248                         return !mVisibilityLocationProvider.isInVisibleLocation(entry);
249                     }
250                     return false;
251                 }
252 
253                 private boolean isParentHeadsUpGroup(NotificationEntry entry) {
254                     if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
255                         return false;
256                     }
257                     if (entry == null) {
258                         return false;
259                     }
260 
261                     final PipelineEntry parent = entry.getParent();
262 
263                     if (parent == null) {
264                         return false;
265                     }
266                     return isHeadsUpGroup(parent);
267                 }
268 
269                 private boolean isHeadsUpGroup(PipelineEntry pipelineEntry) {
270                     if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
271                         return false;
272                     }
273 
274                     if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
275                         return false;
276                     }
277 
278                     final NotificationEntry summary = groupEntry.getSummary();
279                     if (summary == null) {
280                         return false;
281                     }
282 
283                     return mHeadsUpRepository.isHeadsUpEntry(summary.getKey());
284                 }
285                 /**
286                  * When reordering is enabled, non-heads-up groups can be pruned.
287                  * @return true if the given group entry can be pruned.
288                  */
289                 private boolean canReorderGroupEntry(GroupEntry entry) {
290                     if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
291                         return false;
292                     }
293 
294                     return entry != null && mReorderingAllowed && !isHeadsUpGroup(entry);
295                 }
296 
297                 /**
298                  * When reordering is enabled, notifications in non-heads-up groups notifications
299                  * are allowed to change.
300                  * @return true if the given notification entry can changed.
301                  */
302                 private boolean canReorderNotificationEntry(NotificationEntry entry) {
303                     if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
304                         return false;
305                     }
306 
307                     return entry != null && mReorderingAllowed && !isParentHeadsUpGroup(entry);
308                 }
309 
310                 @Override
311                 public void onBeginRun() {
312                     mIsSuppressingPipelineRun = false;
313                     mIsSuppressingGroupChange = false;
314                     mEntriesWithSuppressedSectionChange.clear();
315                     mIsSuppressingEntryReorder = false;
316                 }
317 
318                 @Override
319                 public boolean isPipelineRunAllowed() {
320                     mIsSuppressingPipelineRun |= !mPipelineRunAllowed;
321                     return mPipelineRunAllowed;
322                 }
323 
324                 @Override
325                 public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
326                     final boolean isGroupChangeAllowedForEntry;
327                     if (StabilizeHeadsUpGroup.isEnabled()) {
328                         isGroupChangeAllowedForEntry =
329                                 isEveryChangeAllowed()
330                                         || canReorderNotificationEntry(entry)
331                                         || canMoveForHeadsUp(entry);
332                     } else {
333                         isGroupChangeAllowedForEntry = mReorderingAllowed
334                                 || canMoveForHeadsUp(entry);
335                     }
336                     mIsSuppressingGroupChange |= !isGroupChangeAllowedForEntry;
337                     return isGroupChangeAllowedForEntry;
338                 }
339 
340                 @Override
341                 public boolean isGroupPruneAllowed(@NonNull GroupEntry entry) {
342                     boolean isGroupPruneAllowedForEntry;
343                     if (StabilizeHeadsUpGroup.isEnabled()) {
344                         isGroupPruneAllowedForEntry = isEveryChangeAllowed()
345                                 || canReorderGroupEntry(entry);
346                     } else {
347                         isGroupPruneAllowedForEntry = mReorderingAllowed;
348                     }
349 
350                     mIsSuppressingGroupChange |= !isGroupPruneAllowedForEntry;
351                     return isGroupPruneAllowedForEntry;
352                 }
353 
354                 @Override
355                 public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
356                     final boolean isSectionChangeAllowedForEntry;
357                     if (StabilizeHeadsUpGroup.isEnabled()) {
358                         isSectionChangeAllowedForEntry =
359                                 isEveryChangeAllowed()
360                                         || canReorderNotificationEntry(entry)
361                                         || canMoveForHeadsUp(entry)
362                                         || mEntriesThatCanChangeSection.containsKey(entry.getKey());
363                     } else {
364                         isSectionChangeAllowedForEntry =
365                                 mReorderingAllowed
366                                         || canMoveForHeadsUp(entry)
367                                         || mEntriesThatCanChangeSection.containsKey(entry.getKey());
368                     }
369 
370                     if (!isSectionChangeAllowedForEntry) {
371                         mEntriesWithSuppressedSectionChange.add(entry.getKey());
372                     }
373                     return isSectionChangeAllowedForEntry;
374                 }
375 
376                 @Override
377                 public boolean isEntryReorderingAllowed(@NonNull PipelineEntry entry) {
378                     if (StabilizeHeadsUpGroup.isEnabled()) {
379                         if (isEveryChangeAllowed()) {
380                             return true;
381                         }
382 
383                         final NotificationEntry notificationEntry = entry.getRepresentativeEntry();
384                         return canReorderNotificationEntry(notificationEntry)
385                                 || canMoveForHeadsUp(notificationEntry);
386                     } else {
387                         return mReorderingAllowed || canMoveForHeadsUp(
388                                 entry.getRepresentativeEntry());
389                     }
390                 }
391 
392                 @Override
393                 public boolean isEveryChangeAllowed() {
394                     if (StabilizeHeadsUpGroup.isEnabled()) {
395                         return mReorderingAllowed && mHeadsUpGroupKeys.isEmpty();
396                     } else {
397                         return mReorderingAllowed;
398                     }
399                 }
400 
401                 @Override
402                 public void onEntryReorderSuppressed() {
403                     mIsSuppressingEntryReorder = true;
404                 }
405             };
406 
407     private final OnBeforeRenderListListener mOnBeforeRenderListListener =
408             new OnBeforeRenderListListener() {
409                 @Override
410                 public void onBeforeRenderList(List<PipelineEntry> entries) {
411                     if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
412                         return;
413                     }
414 
415                     final Set<String> currentHeadsUpKeys = new HashSet<>();
416 
417                     for (int i = 0; i < entries.size(); i++) {
418                         if (entries.get(i) instanceof GroupEntry groupEntry) {
419                             final NotificationEntry summary = groupEntry.getSummary();
420                             if (summary == null) continue;
421 
422                             final String summaryKey = summary.getKey();
423                             if (mHeadsUpRepository.isHeadsUpEntry(summaryKey)) {
424                                 currentHeadsUpKeys.add(summaryKey);
425                             }
426                         }
427                     }
428 
429                     setHeadsUpGroupKeys(currentHeadsUpKeys);
430                 }
431             };
432 
updateAllowedStates(String field, boolean value)433     private void updateAllowedStates(String field, boolean value) {
434         updateAllowedStates(field, value, /* async = */ false);
435     }
436 
updateAllowedStates(String field, boolean value, boolean async)437     private void updateAllowedStates(String field, boolean value, boolean async) {
438         boolean wasPipelineRunAllowed = mPipelineRunAllowed;
439         boolean wasReorderingAllowed = mReorderingAllowed;
440         // No need to run notification pipeline when the lockscreen is in fading animation.
441         mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
442                 || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
443         mReorderingAllowed = isReorderingAllowed();
444         if (wasPipelineRunAllowed != mPipelineRunAllowed
445                 || wasReorderingAllowed != mReorderingAllowed) {
446             mLogger.logAllowancesChanged(
447                     wasPipelineRunAllowed, mPipelineRunAllowed,
448                     wasReorderingAllowed, mReorderingAllowed,
449                     field, value, async);
450         }
451         if (async) {
452             mMainExecutor.execute(this::maybeInvalidateList);
453         } else {
454             maybeInvalidateList();
455         }
456         mVisualStabilityProvider.setReorderingAllowed(mReorderingAllowed);
457     }
458 
maybeInvalidateList()459     private void maybeInvalidateList() {
460         if (mPipelineRunAllowed && mIsSuppressingPipelineRun) {
461             mNotifStabilityManager.invalidateList("pipeline run suppression ended");
462         } else if (mReorderingAllowed && (mIsSuppressingGroupChange
463                 || isSuppressingSectionChange()
464                 || mIsSuppressingEntryReorder)) {
465             final String reason = "reorder suppression ended for"
466                     + " group=" + mIsSuppressingGroupChange
467                     + " section=" + isSuppressingSectionChange()
468                     + " sort=" + mIsSuppressingEntryReorder;
469             mNotifStabilityManager.invalidateList(reason);
470         }
471     }
472 
isSuppressingSectionChange()473     private boolean isSuppressingSectionChange() {
474         return !mEntriesWithSuppressedSectionChange.isEmpty();
475     }
476 
isPanelCollapsingOrLaunchingActivity()477     private boolean isPanelCollapsingOrLaunchingActivity() {
478         return mNotifPanelCollapsing || mNotifPanelLaunchingActivity;
479     }
480 
isReorderingAllowed()481     private boolean isReorderingAllowed() {
482         final boolean sleepyAndDozed = mFullyDozed && mSleepy;
483         final boolean stackShowing = isStackShowing();
484         return (sleepyAndDozed || !stackShowing || mCommunalShowing) && !mPulsing;
485     }
486 
487     /** @return true when the notification stack is visible to the user */
isStackShowing()488     private boolean isStackShowing() {
489         if (SceneContainerFlag.isEnabled()) {
490             return mPanelExpanded || mLockscreenShowing || mTrackingHeadsUp;
491         } else {
492             return mPanelExpanded;
493         }
494     }
495 
496     /**
497      * Allows this notification entry to be re-ordered in the notification list temporarily until
498      * the timeout has passed.
499      *
500      * Typically this is allowed because the user has directly changed something about the
501      * notification and we are reordering based on the user's change.
502      *
503      * @param entry notification entry that can change sections even if isReorderingAllowed is false
504      * @param now current time SystemClock.elapsedRealtime
505      */
temporarilyAllowSectionChanges(@onNull NotificationEntry entry, long now)506     public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
507         final String entryKey = entry.getKey();
508         final boolean wasSectionChangeAllowed =
509                 mNotifStabilityManager.isSectionChangeAllowed(entry);
510 
511         // If it exists, cancel previous timeout
512         if (mEntriesThatCanChangeSection.containsKey(entryKey)) {
513             mEntriesThatCanChangeSection.get(entryKey).run();
514         }
515 
516         // Schedule & store new timeout cancellable
517         mEntriesThatCanChangeSection.put(
518                 entryKey,
519                 mDelayableExecutor.executeAtTime(
520                         () -> mEntriesThatCanChangeSection.remove(entryKey),
521                         now + ALLOW_SECTION_CHANGE_TIMEOUT));
522 
523         if (!wasSectionChangeAllowed) {
524             mNotifStabilityManager.invalidateList("temporarilyAllowSectionChanges");
525         }
526     }
527 
528     final StatusBarStateController.StateListener mStatusBarStateControllerListener =
529             new StatusBarStateController.StateListener() {
530                 @Override
531                 public void onPulsingChanged(boolean pulsing) {
532                     mPulsing = pulsing;
533                     updateAllowedStates("pulsing", pulsing);
534                 }
535 
536                 @Override
537                 public void onExpandedChanged(boolean expanded) {
538                     mPanelExpanded = expanded;
539                     updateAllowedStates("panelExpanded", expanded);
540                 }
541 
542                 @Override
543                 public void onDozeAmountChanged(float linear, float eased) {
544                     final boolean fullyDozed = linear == 1f;
545                     mFullyDozed = fullyDozed;
546                     updateAllowedStates("fullyDozed", fullyDozed);
547                 }
548             };
549     final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
550         @Override
551         public void onFinishedGoingToSleep() {
552             // NOTE: this method is called much earlier than what we consider "finished" going to
553             // sleep (the animation isn't done), so we also need to check the doze amount is not 1
554             // and use the combo to determine that the locked shade is not visible.
555             mSleepy = true;
556             updateAllowedStates("sleepy", true);
557         }
558 
559         @Override
560         public void onStartedWakingUp() {
561             mSleepy = false;
562             updateAllowedStates("sleepy", false);
563         }
564     };
565 
566     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)567     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
568         pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
569         pw.println("  notifPanelCollapsing: " + mNotifPanelCollapsing);
570         pw.println("  launchingNotifActivity: " + mNotifPanelLaunchingActivity);
571         if (mCheckLockScreenTransitionEnabled) {
572             pw.println("  lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
573         }
574         pw.println("reorderingAllowed: " + mReorderingAllowed);
575         pw.println("  sleepy: " + mSleepy);
576         pw.println("  fullyDozed: " + mFullyDozed);
577         pw.println("  panelExpanded: " + mPanelExpanded);
578         pw.println("  pulsing: " + mPulsing);
579         pw.println("  communalShowing: " + mCommunalShowing);
580         pw.println("isSuppressingPipelineRun: " + mIsSuppressingPipelineRun);
581         pw.println("isSuppressingGroupChange: " + mIsSuppressingGroupChange);
582         pw.println("isSuppressingEntryReorder: " + mIsSuppressingEntryReorder);
583         if (StabilizeHeadsUpGroup.isEnabled()) {
584             pw.println("headsUpGroupKeys: " + mHeadsUpGroupKeys.size());
585         }
586         pw.println("entriesWithSuppressedSectionChange: "
587                 + mEntriesWithSuppressedSectionChange.size());
588         for (String key : mEntriesWithSuppressedSectionChange) {
589             pw.println("  " + key);
590         }
591         pw.println("entriesThatCanChangeSection: " + mEntriesThatCanChangeSection.size());
592         for (String key : mEntriesThatCanChangeSection.keySet()) {
593             pw.println("  " + key);
594         }
595     }
596 
onShadeOrQsClosingChanged(boolean isClosing)597     private void onShadeOrQsClosingChanged(boolean isClosing) {
598         mNotifPanelCollapsing = isClosing;
599         updateAllowedStates("notifPanelCollapsing", isClosing);
600     }
601 
onLaunchingActivityChanged(boolean isLaunchingActivity)602     private void onLaunchingActivityChanged(boolean isLaunchingActivity) {
603         mNotifPanelLaunchingActivity = isLaunchingActivity;
604         updateAllowedStates("notifPanelLaunchingActivity", isLaunchingActivity);
605     }
606 
onCommunalShowingChanged(boolean isShowing)607     private void onCommunalShowingChanged(boolean isShowing) {
608         mCommunalShowing = isShowing;
609         updateAllowedStates("communalShowing", isShowing);
610     }
611 
onLockscreenKeyguardStateTransitionValueChanged(float value)612     private void onLockscreenKeyguardStateTransitionValueChanged(float value) {
613         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
614             return;
615         }
616 
617         final boolean isShowing = value > 0.0f;
618         if (isShowing == mLockscreenShowing) {
619             return;
620         }
621 
622         mLockscreenShowing = isShowing;
623         updateAllowedStates("lockscreenShowing", isShowing);
624     }
625 
onTrackingHeadsUpModeChanged(boolean isTrackingHeadsUp)626     private void onTrackingHeadsUpModeChanged(boolean isTrackingHeadsUp) {
627         mTrackingHeadsUp = isTrackingHeadsUp;
628         updateAllowedStates("trackingHeadsUp", isTrackingHeadsUp);
629     }
630 
onLockscreenInGoneTransitionChanged(boolean inGoneTransition)631     private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
632         if (!mCheckLockScreenTransitionEnabled) {
633             return;
634         }
635         if (inGoneTransition == mLockscreenInGoneTransition) {
636             return;
637         }
638         mLockscreenInGoneTransition = inGoneTransition;
639         updateAllowedStates("lockscreenInGoneTransition", mLockscreenInGoneTransition);
640     }
641 }
642