• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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
18 
19 import android.util.FloatProperty
20 import android.view.animation.Interpolator
21 import androidx.annotation.VisibleForTesting
22 import androidx.core.animation.ObjectAnimator
23 import com.android.systemui.Dumpable
24 import com.android.systemui.animation.Interpolators
25 import com.android.systemui.animation.InterpolatorsAndroidX
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dump.DumpManager
28 import com.android.systemui.plugins.statusbar.StatusBarStateController
29 import com.android.systemui.shade.NotificationPanelViewController.WAKEUP_ANIMATION_DELAY_MS
30 import com.android.systemui.shade.ShadeExpansionChangeEvent
31 import com.android.systemui.shade.ShadeExpansionListener
32 import com.android.systemui.statusbar.StatusBarState
33 import com.android.systemui.statusbar.notification.collection.NotificationEntry
34 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
35 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
36 import com.android.systemui.statusbar.phone.DozeParameters
37 import com.android.systemui.statusbar.phone.KeyguardBypassController
38 import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassStateChangedListener
39 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
40 import com.android.systemui.statusbar.policy.HeadsUpManager
41 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
42 import com.android.systemui.util.doOnEnd
43 import com.android.systemui.util.doOnStart
44 import java.io.PrintWriter
45 import javax.inject.Inject
46 import kotlin.math.max
47 import kotlin.math.min
48 
49 @SysUISingleton
50 class NotificationWakeUpCoordinator
51 @Inject
52 constructor(
53     dumpManager: DumpManager,
54     private val mHeadsUpManager: HeadsUpManager,
55     private val statusBarStateController: StatusBarStateController,
56     private val bypassController: KeyguardBypassController,
57     private val dozeParameters: DozeParameters,
58     private val screenOffAnimationController: ScreenOffAnimationController,
59     private val logger: NotificationWakeUpCoordinatorLogger,
60 ) :
61     OnHeadsUpChangedListener,
62     StatusBarStateController.StateListener,
63     ShadeExpansionListener,
64     Dumpable {
65     private lateinit var mStackScrollerController: NotificationStackScrollLayoutController
66     private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
67 
68     private var inputLinearDozeAmount: Float = 0.0f
69     private var inputEasedDozeAmount: Float = 0.0f
70     private var delayedDozeAmountOverride: Float = 0.0f
71     private var delayedDozeAmountAnimator: ObjectAnimator? = null
72     /** Valid values: {1f, 0f, null} null => use input */
73     private var hardDozeAmountOverride: Float? = null
74     private var hardDozeAmountOverrideSource: String = "n/a"
75     private var outputLinearDozeAmount: Float = 0.0f
76     private var outputEasedDozeAmount: Float = 0.0f
77     @VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN
78 
79     private var mNotificationVisibleAmount = 0.0f
80     private var mNotificationsVisible = false
81     private var mNotificationsVisibleForExpansion = false
82     private var mVisibilityAnimator: ObjectAnimator? = null
83     private var mVisibilityAmount = 0.0f
84     private var mLinearVisibilityAmount = 0.0f
85     private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
86     private var pulseExpanding: Boolean = false
87     private val wakeUpListeners = arrayListOf<WakeUpListener>()
88     private var state: Int = StatusBarState.KEYGUARD
89 
90     var fullyAwake: Boolean = false
91 
92     var wakingUp = false
93         private set(value) {
94             field = value
95             willWakeUp = false
96             if (value) {
97                 if (
98                     mNotificationsVisible &&
99                         !mNotificationsVisibleForExpansion &&
100                         !bypassController.bypassEnabled
101                 ) {
102                     // We're waking up while pulsing, let's make sure the animation looks nice
103                     mStackScrollerController.wakeUpFromPulse()
104                 }
105                 if (bypassController.bypassEnabled && !mNotificationsVisible) {
106                     // Let's make sure our huns become visible once we are waking up in case
107                     // they were blocked by the proximity sensor
108                     updateNotificationVisibility(
109                         animate = shouldAnimateVisibility(),
110                         increaseSpeed = false
111                     )
112                 }
113             }
114         }
115 
116     var willWakeUp = false
117         set(value) {
118             if (!value || outputLinearDozeAmount != 0.0f) {
119                 field = value
120             }
121         }
122 
123     private var collapsedEnoughToHide: Boolean = false
124 
125     var pulsing: Boolean = false
126         set(value) {
127             field = value
128             if (value) {
129                 // Only when setting pulsing to true we want an immediate update, since we get
130                 // this already when the doze service finishes which is usually before we get
131                 // the waking up callback
132                 updateNotificationVisibility(
133                     animate = shouldAnimateVisibility(),
134                     increaseSpeed = false
135                 )
136             }
137         }
138 
139     var notificationsFullyHidden: Boolean = false
140         private set(value) {
141             if (field != value) {
142                 field = value
143                 for (listener in wakeUpListeners) {
144                     listener.onFullyHiddenChanged(value)
145                 }
146             }
147         }
148 
149     /** True if we can show pulsing heads up notifications */
150     var canShowPulsingHuns: Boolean = false
151         private set
152         get() {
153             var canShow = pulsing
154             if (bypassController.bypassEnabled) {
155                 // We also allow pulsing on the lock screen!
156                 canShow =
157                     canShow ||
158                         (wakingUp || willWakeUp || fullyAwake) &&
159                             statusBarStateController.state == StatusBarState.KEYGUARD
160                 // We want to hide the notifications when collapsed too much
161                 if (collapsedEnoughToHide) {
162                     canShow = false
163                 }
164             }
165             return canShow
166         }
167 
168     private val bypassStateChangedListener =
169         object : OnBypassStateChangedListener {
onBypassStateChangednull170             override fun onBypassStateChanged(isEnabled: Boolean) {
171                 // When the bypass state changes, we have to check whether we should re-show the
172                 // notifications by clearing the doze amount override which hides them.
173                 maybeClearHardDozeAmountOverrideHidingNotifs()
174             }
175         }
176 
177     init {
178         dumpManager.registerDumpable(this)
179         mHeadsUpManager.addListener(this)
180         statusBarStateController.addCallback(this)
181         bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
182         addListener(
183             object : WakeUpListener {
onFullyHiddenChangednull184                 override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
185                     if (isFullyHidden && mNotificationsVisibleForExpansion) {
186                         // When the notification becomes fully invisible, let's make sure our
187                         // expansion
188                         // flag also changes. This can happen if the bouncer shows when dragging
189                         // down
190                         // and then the screen turning off, where we don't reset this state.
191                         setNotificationsVisibleForExpansion(
192                             visible = false,
193                             animate = false,
194                             increaseSpeed = false
195                         )
196                     }
197                 }
198             }
199         )
200     }
201 
setStackScrollernull202     fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
203         mStackScrollerController = stackScrollerController
204         pulseExpanding = stackScrollerController.isPulseExpanding
205         stackScrollerController.setOnPulseHeightChangedListener {
206             val nowExpanding = isPulseExpanding()
207             val changed = nowExpanding != pulseExpanding
208             pulseExpanding = nowExpanding
209             for (listener in wakeUpListeners) {
210                 listener.onPulseExpansionChanged(changed)
211             }
212         }
213     }
214 
isPulseExpandingnull215     fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding
216 
217     /**
218      * @param visible should notifications be visible
219      * @param animate should this change be animated
220      * @param increaseSpeed should the speed be increased of the animation
221      */
222     fun setNotificationsVisibleForExpansion(
223         visible: Boolean,
224         animate: Boolean,
225         increaseSpeed: Boolean
226     ) {
227         mNotificationsVisibleForExpansion = visible
228         updateNotificationVisibility(animate, increaseSpeed)
229         if (!visible && mNotificationsVisible) {
230             // If we stopped expanding and we're still visible because we had a pulse that hasn't
231             // times out, let's release them all to make sure were not stuck in a state where
232             // notifications are visible
233             mHeadsUpManager.releaseAllImmediately()
234         }
235     }
236 
addListenernull237     fun addListener(listener: WakeUpListener) {
238         wakeUpListeners.add(listener)
239     }
240 
removeListenernull241     fun removeListener(listener: WakeUpListener) {
242         wakeUpListeners.remove(listener)
243     }
244 
updateNotificationVisibilitynull245     private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
246         // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
247         var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
248         visible = visible && canShowPulsingHuns
249 
250         if (
251             !visible &&
252                 mNotificationsVisible &&
253                 (wakingUp || willWakeUp) &&
254                 outputLinearDozeAmount != 0.0f
255         ) {
256             // let's not make notifications invisible while waking up, otherwise the animation
257             // is strange
258             return
259         }
260         setNotificationsVisible(visible, animate, increaseSpeed)
261     }
262 
setNotificationsVisiblenull263     private fun setNotificationsVisible(
264         visible: Boolean,
265         animate: Boolean,
266         increaseSpeed: Boolean
267     ) {
268         if (mNotificationsVisible == visible) {
269             return
270         }
271         mNotificationsVisible = visible
272         mVisibilityAnimator?.cancel()
273         if (animate) {
274             notifyAnimationStart(visible)
275             startVisibilityAnimation(increaseSpeed)
276         } else {
277             setVisibilityAmount(if (visible) 1.0f else 0.0f)
278         }
279     }
280 
onDozeAmountChangednull281     override fun onDozeAmountChanged(linear: Float, eased: Float) {
282         logger.logOnDozeAmountChanged(linear = linear, eased = eased)
283         inputLinearDozeAmount = linear
284         inputEasedDozeAmount = eased
285         if (overrideDozeAmountIfAnimatingScreenOff()) {
286             return
287         }
288 
289         if (overrideDozeAmountIfBypass()) {
290             return
291         }
292 
293         if (clearHardDozeAmountOverride()) {
294             return
295         }
296 
297         updateDozeAmount()
298     }
299 
setHardDozeAmountOverridenull300     private fun setHardDozeAmountOverride(dozing: Boolean, source: String) {
301         logger.logSetDozeAmountOverride(dozing = dozing, source = source)
302         hardDozeAmountOverride = if (dozing) 1f else 0f
303         hardDozeAmountOverrideSource = source
304         updateDozeAmount()
305     }
306 
clearHardDozeAmountOverridenull307     private fun clearHardDozeAmountOverride(): Boolean {
308         if (hardDozeAmountOverride == null) return false
309         hardDozeAmountOverride = null
310         hardDozeAmountOverrideSource = "Cleared: $hardDozeAmountOverrideSource"
311         updateDozeAmount()
312         return true
313     }
314 
updateDozeAmountnull315     private fun updateDozeAmount() {
316         // Calculate new doze amount (linear)
317         val newOutputLinearDozeAmount =
318             hardDozeAmountOverride ?: max(inputLinearDozeAmount, delayedDozeAmountOverride)
319         val changed = outputLinearDozeAmount != newOutputLinearDozeAmount
320 
321         // notify when the animation is starting
322         if (
323             newOutputLinearDozeAmount != 1.0f &&
324                 newOutputLinearDozeAmount != 0.0f &&
325                 (outputLinearDozeAmount == 0.0f || outputLinearDozeAmount == 1.0f)
326         ) {
327             // Let's notify the scroller that an animation started
328             notifyAnimationStart(outputLinearDozeAmount == 1.0f)
329         }
330 
331         // Update output doze amount
332         outputLinearDozeAmount = newOutputLinearDozeAmount
333         outputEasedDozeAmount = dozeAmountInterpolator.getInterpolation(outputLinearDozeAmount)
334         logger.logUpdateDozeAmount(
335             inputLinear = inputLinearDozeAmount,
336             delayLinear = delayedDozeAmountOverride,
337             hardOverride = hardDozeAmountOverride,
338             outputLinear = outputLinearDozeAmount,
339             state = statusBarStateController.state,
340             changed = changed
341         )
342         mStackScrollerController.setDozeAmount(outputEasedDozeAmount)
343         updateHideAmount()
344         if (changed && outputLinearDozeAmount == 0.0f) {
345             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
346             setNotificationsVisibleForExpansion(
347                 visible = false,
348                 animate = false,
349                 increaseSpeed = false
350             )
351         }
352     }
353 
354     /**
355      * Notifies the wakeup coordinator that we're waking up.
356      *
357      * [requestDelayedAnimation] is used to request that we delay the start of the wakeup animation
358      * in order to wait for a potential fingerprint authentication to arrive, since unlocking during
359      * the wakeup animation looks chaotic.
360      *
361      * If called with [wakingUp] and [requestDelayedAnimation] both `true`, the [WakeUpListener]s
362      * are guaranteed to receive at least one [WakeUpListener.onDelayedDozeAmountAnimationRunning]
363      * call with `false` at some point in the near future. A call with `true` before that will
364      * happen if the animation is not already running.
365      */
setWakingUpnull366     fun setWakingUp(
367         wakingUp: Boolean,
368         requestDelayedAnimation: Boolean,
369     ) {
370         logger.logSetWakingUp(wakingUp, requestDelayedAnimation)
371         this.wakingUp = wakingUp
372         if (wakingUp && requestDelayedAnimation) {
373             scheduleDelayedDozeAmountAnimation()
374         }
375     }
376 
scheduleDelayedDozeAmountAnimationnull377     private fun scheduleDelayedDozeAmountAnimation() {
378         val alreadyRunning = delayedDozeAmountAnimator != null
379         logger.logStartDelayedDozeAmountAnimation(alreadyRunning)
380         if (alreadyRunning) return
381         delayedDozeAmount.setValue(this, 1.0f)
382         delayedDozeAmountAnimator =
383             ObjectAnimator.ofFloat(this, delayedDozeAmount, 0.0f).apply {
384                 interpolator = InterpolatorsAndroidX.LINEAR
385                 duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
386                 startDelay = WAKEUP_ANIMATION_DELAY_MS.toLong()
387                 doOnStart {
388                     wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(true) }
389                 }
390                 doOnEnd {
391                     delayedDozeAmountAnimator = null
392                     wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(false) }
393                 }
394                 start()
395             }
396     }
397 
onStateChangednull398     override fun onStateChanged(newState: Int) {
399         logger.logOnStateChanged(newState = newState, storedState = state)
400         if (state == StatusBarState.SHADE && newState == StatusBarState.SHADE) {
401             // The SHADE -> SHADE transition is only possible as part of cancelling the screen-off
402             // animation (e.g. by fingerprint unlock).  This is done because the system is in an
403             // undefined state, so it's an indication that we should do state cleanup. We override
404             // the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
405             // See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
406             setHardDozeAmountOverride(
407                 dozing = false,
408                 source = "Override: Shade->Shade (lock cancelled by unlock)"
409             )
410             this.state = newState
411             return
412         }
413 
414         if (overrideDozeAmountIfAnimatingScreenOff()) {
415             this.state = newState
416             return
417         }
418 
419         if (overrideDozeAmountIfBypass()) {
420             this.state = newState
421             return
422         }
423 
424         maybeClearHardDozeAmountOverrideHidingNotifs()
425 
426         this.state = newState
427     }
428 
429     @VisibleForTesting
430     val statusBarState: Int
431         get() = state
432 
onPanelExpansionChangednull433     override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
434         val collapsedEnough = event.fraction <= 0.9f
435         if (collapsedEnough != this.collapsedEnoughToHide) {
436             val couldShowPulsingHuns = canShowPulsingHuns
437             this.collapsedEnoughToHide = collapsedEnough
438             if (couldShowPulsingHuns && !canShowPulsingHuns) {
439                 updateNotificationVisibility(animate = true, increaseSpeed = true)
440                 mHeadsUpManager.releaseAllImmediately()
441             }
442         }
443     }
444 
445     /**
446      * @return Whether the doze amount was overridden because bypass is enabled. If true, the
447      *   original doze amount should be ignored.
448      */
overrideDozeAmountIfBypassnull449     private fun overrideDozeAmountIfBypass(): Boolean {
450         if (bypassController.bypassEnabled) {
451             if (statusBarStateController.state == StatusBarState.KEYGUARD) {
452                 setHardDozeAmountOverride(dozing = true, source = "Override: bypass (keyguard)")
453             } else {
454                 setHardDozeAmountOverride(dozing = false, source = "Override: bypass (shade)")
455             }
456             return true
457         }
458         return false
459     }
460 
461     /**
462      * If the last [setDozeAmount] call was an override to hide notifications, then this call will
463      * check for the set of states that may have caused that override, and if none of them still
464      * apply, and the device is awake or not on the keyguard, then dozeAmount will be reset to 0.
465      * This fixes bugs where the bypass state changing could result in stale overrides, hiding
466      * notifications either on the inside screen or even after unlock.
467      */
maybeClearHardDozeAmountOverrideHidingNotifsnull468     private fun maybeClearHardDozeAmountOverrideHidingNotifs() {
469         if (hardDozeAmountOverride == 1f) {
470             val onKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
471             val dozing = statusBarStateController.isDozing
472             val bypass = bypassController.bypassEnabled
473             val animating =
474                 screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()
475             // Overrides are set by [overrideDozeAmountIfAnimatingScreenOff] and
476             // [overrideDozeAmountIfBypass] based on 'animating' and 'bypass' respectively, so only
477             // clear the override if both those conditions are cleared.  But also require either
478             // !dozing or !onKeyguard because those conditions should indicate that we intend
479             // notifications to be visible, and thus it is safe to unhide them.
480             val willRemove = (!onKeyguard || !dozing) && !bypass && !animating
481             logger.logMaybeClearHardDozeAmountOverrideHidingNotifs(
482                 willRemove = willRemove,
483                 onKeyguard = onKeyguard,
484                 dozing = dozing,
485                 bypass = bypass,
486                 animating = animating,
487             )
488             if (willRemove) {
489                 clearHardDozeAmountOverride()
490             }
491         }
492     }
493 
494     /**
495      * If we're playing the screen off animation, force the notification doze amount to be 1f (fully
496      * dozing). This is needed so that the notifications aren't briefly visible as the screen turns
497      * off and dozeAmount goes from 1f to 0f.
498      *
499      * @return Whether the doze amount was overridden because we are playing the screen off
500      *   animation. If true, the original doze amount should be ignored.
501      */
overrideDozeAmountIfAnimatingScreenOffnull502     private fun overrideDozeAmountIfAnimatingScreenOff(): Boolean {
503         if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
504             setHardDozeAmountOverride(dozing = true, source = "Override: animating screen off")
505             return true
506         }
507 
508         return false
509     }
510 
startVisibilityAnimationnull511     private fun startVisibilityAnimation(increaseSpeed: Boolean) {
512         if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
513             mVisibilityInterpolator =
514                 if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE
515                 else Interpolators.FAST_OUT_SLOW_IN_REVERSE
516         }
517         val target = if (mNotificationsVisible) 1.0f else 0.0f
518         val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target)
519         visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR
520         var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
521         if (increaseSpeed) {
522             duration = (duration.toFloat() / 1.5F).toLong()
523         }
524         visibilityAnimator.duration = duration
525         visibilityAnimator.start()
526         mVisibilityAnimator = visibilityAnimator
527     }
528 
setVisibilityAmountnull529     private fun setVisibilityAmount(visibilityAmount: Float) {
530         logger.logSetVisibilityAmount(visibilityAmount)
531         mLinearVisibilityAmount = visibilityAmount
532         mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount)
533         handleAnimationFinished()
534         updateHideAmount()
535     }
536 
handleAnimationFinishednull537     private fun handleAnimationFinished() {
538         if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
539             mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
540             mEntrySetToClearWhenFinished.clear()
541         }
542     }
543 
updateHideAmountnull544     private fun updateHideAmount() {
545         val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount)
546         val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount)
547         logger.logSetHideAmount(linearAmount)
548         mStackScrollerController.setHideAmount(linearAmount, amount)
549         notificationsFullyHidden = linearAmount == 1.0f
550     }
551 
notifyAnimationStartnull552     private fun notifyAnimationStart(awake: Boolean) {
553         mStackScrollerController.notifyHideAnimationStart(!awake)
554     }
555 
onDozingChangednull556     override fun onDozingChanged(isDozing: Boolean) {
557         if (isDozing) {
558             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
559         }
560     }
561 
onHeadsUpStateChangednull562     override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
563         var animate = shouldAnimateVisibility()
564         if (!isHeadsUp) {
565             if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
566                 if (entry.isRowDismissed) {
567                     // if we animate, we see the shelf briefly visible. Instead we fully animate
568                     // the notification and its background out
569                     animate = false
570                 } else if (!wakingUp && !willWakeUp) {
571                     // TODO: look that this is done properly and not by anyone else
572                     entry.setHeadsUpAnimatingAway(true)
573                     mEntrySetToClearWhenFinished.add(entry)
574                 }
575             }
576         } else if (mEntrySetToClearWhenFinished.contains(entry)) {
577             mEntrySetToClearWhenFinished.remove(entry)
578             entry.setHeadsUpAnimatingAway(false)
579         }
580         updateNotificationVisibility(animate, increaseSpeed = false)
581     }
582 
shouldAnimateVisibilitynull583     private fun shouldAnimateVisibility() =
584         dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
585 
586     override fun dump(pw: PrintWriter, args: Array<out String>) {
587         pw.println("inputLinearDozeAmount: $inputLinearDozeAmount")
588         pw.println("inputEasedDozeAmount: $inputEasedDozeAmount")
589         pw.println("delayedDozeAmountOverride: $delayedDozeAmountOverride")
590         pw.println("hardDozeAmountOverride: $hardDozeAmountOverride")
591         pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource")
592         pw.println("outputLinearDozeAmount: $outputLinearDozeAmount")
593         pw.println("outputEasedDozeAmount: $outputEasedDozeAmount")
594         pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
595         pw.println("mNotificationsVisible: $mNotificationsVisible")
596         pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
597         pw.println("mVisibilityAmount: $mVisibilityAmount")
598         pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
599         pw.println("pulseExpanding: $pulseExpanding")
600         pw.println("state: ${StatusBarState.toString(state)}")
601         pw.println("fullyAwake: $fullyAwake")
602         pw.println("wakingUp: $wakingUp")
603         pw.println("willWakeUp: $willWakeUp")
604         pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
605         pw.println("pulsing: $pulsing")
606         pw.println("notificationsFullyHidden: $notificationsFullyHidden")
607         pw.println("canShowPulsingHuns: $canShowPulsingHuns")
608     }
609 
logDelayingClockWakeUpAnimationnull610     fun logDelayingClockWakeUpAnimation(delayingAnimation: Boolean) {
611         logger.logDelayingClockWakeUpAnimation(delayingAnimation)
612     }
613 
614     interface WakeUpListener {
615         /** Called whenever the notifications are fully hidden or shown */
onFullyHiddenChangednull616         @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
617 
618         /**
619          * Called whenever the pulseExpansion changes
620          *
621          * @param expandingChanged if the user has started or stopped expanding
622          */
onPulseExpansionChangednull623         @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
624 
625         /**
626          * Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
627          * after the start delay, or after it ends/is cancelled.
628          */
onDelayedDozeAmountAnimationRunningnull629         @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
630     }
631 
632     companion object {
633         private val notificationVisibility =
634             object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
635 
setValuenull636                 override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
637                     coordinator.setVisibilityAmount(value)
638                 }
639 
getnull640                 override fun get(coordinator: NotificationWakeUpCoordinator): Float {
641                     return coordinator.mLinearVisibilityAmount
642                 }
643             }
644 
645         private val delayedDozeAmount =
646             object : FloatProperty<NotificationWakeUpCoordinator>("delayedDozeAmount") {
647 
setValuenull648                 override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
649                     coordinator.delayedDozeAmountOverride = value
650                     coordinator.logger.logSetDelayDozeAmountOverride(value)
651                     coordinator.updateDozeAmount()
652                 }
653 
getnull654                 override fun get(coordinator: NotificationWakeUpCoordinator): Float {
655                     return coordinator.delayedDozeAmountOverride
656                 }
657             }
658     }
659 }
660