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