1 /* 2 * Copyright (C) 2021 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 package com.android.systemui.statusbar.phone 17 18 import android.view.View 19 import com.android.systemui.dagger.SysUISingleton 20 import com.android.systemui.keyguard.WakefulnessLifecycle 21 import com.android.systemui.shade.ShadeViewController 22 import com.android.systemui.statusbar.LightRevealScrim 23 import com.android.systemui.unfold.FoldAodAnimationController 24 import com.android.systemui.unfold.SysUIUnfoldComponent 25 import java.util.Optional 26 import javax.inject.Inject 27 28 @SysUISingleton 29 class ScreenOffAnimationController @Inject constructor( 30 sysUiUnfoldComponent: Optional<SysUIUnfoldComponent>, 31 unlockedScreenOffAnimation: UnlockedScreenOffAnimationController, 32 private val wakefulnessLifecycle: WakefulnessLifecycle, 33 ) : WakefulnessLifecycle.Observer { 34 35 private val foldToAodAnimation: FoldAodAnimationController? = sysUiUnfoldComponent 36 .orElse(null)?.getFoldAodAnimationController() 37 38 private val animations: List<ScreenOffAnimation> = 39 listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation) 40 initializenull41 fun initialize( 42 centralSurfaces: CentralSurfaces, 43 shadeViewController: ShadeViewController, 44 lightRevealScrim: LightRevealScrim, 45 ) { 46 animations.forEach { it.initialize(centralSurfaces, shadeViewController, lightRevealScrim) } 47 wakefulnessLifecycle.addObserver(this) 48 } 49 50 /** 51 * Called when system reports that we are going to sleep 52 */ onStartedGoingToSleepnull53 override fun onStartedGoingToSleep() { 54 animations.firstOrNull { it.startAnimation() } 55 } 56 57 /** 58 * Called when opaqueness of the light reveal scrim has change 59 * When [isOpaque] is true then scrim is visible and covers the screen 60 */ onScrimOpaqueChangednull61 fun onScrimOpaqueChanged(isOpaque: Boolean) = 62 animations.forEach { it.onScrimOpaqueChanged(isOpaque) } 63 64 /** 65 * Called when always on display setting changed 66 */ onAlwaysOnChangednull67 fun onAlwaysOnChanged(alwaysOn: Boolean) = 68 animations.forEach { it.onAlwaysOnChanged(alwaysOn) } 69 70 /** 71 * If returns true we are taking over the screen off animation from display manager to SysUI. 72 * We can play our custom animation instead of default fade out animation. 73 */ shouldControlUnlockedScreenOffnull74 fun shouldControlUnlockedScreenOff(): Boolean = 75 animations.any { it.shouldPlayAnimation() } 76 77 /** 78 * If returns true it fully expands notification shade, it could be used to make 79 * the scrims visible 80 */ shouldExpandNotificationsnull81 fun shouldExpandNotifications(): Boolean = 82 animations.any { it.isAnimationPlaying() } 83 84 /** 85 * If returns true it allows to perform custom animation for showing 86 * keyguard in [animateInKeyguard] 87 */ shouldAnimateInKeyguardnull88 fun shouldAnimateInKeyguard(): Boolean = 89 animations.any { it.shouldAnimateInKeyguard() } 90 91 /** 92 * Called when keyguard is about to be displayed and allows to perform custom animation 93 */ animateInKeyguardnull94 fun animateInKeyguard(keyguardView: View, after: Runnable) = 95 animations.firstOrNull { 96 if (it.shouldAnimateInKeyguard()) { 97 it.animateInKeyguard(keyguardView, after) 98 true 99 } else { 100 false 101 } 102 } 103 104 /** 105 * If returns true it will disable propagating touches to apps and keyguard 106 */ shouldIgnoreKeyguardTouchesnull107 fun shouldIgnoreKeyguardTouches(): Boolean = 108 animations.any { it.isAnimationPlaying() } 109 110 /** 111 * If returns true wake up won't be blocked when dozing 112 */ allowWakeUpIfDozingnull113 fun allowWakeUpIfDozing(): Boolean = 114 animations.all { !it.isAnimationPlaying() } 115 116 /** 117 * Do not allow showing keyguard immediately so it could be postponed e.g. to the point when 118 * the animation ends 119 */ shouldDelayKeyguardShownull120 fun shouldDelayKeyguardShow(): Boolean = 121 animations.any { it.shouldDelayKeyguardShow() } 122 123 /** 124 * Return true while we want to ignore requests to show keyguard, we need to handle pending 125 * keyguard lock requests manually 126 * 127 * @see [com.android.systemui.keyguard.KeyguardViewMediator.maybeHandlePendingLock] 128 */ isKeyguardShowDelayednull129 fun isKeyguardShowDelayed(): Boolean = 130 animations.any { it.isKeyguardShowDelayed() } 131 132 /** 133 * Return true to ignore requests to hide keyguard 134 */ isKeyguardHideDelayednull135 fun isKeyguardHideDelayed(): Boolean = 136 animations.any { it.isKeyguardHideDelayed() } 137 138 /** 139 * Return true to make the status bar expanded so we can animate [LightRevealScrim] 140 */ shouldShowLightRevealScrimnull141 fun shouldShowLightRevealScrim(): Boolean = 142 animations.any { it.shouldPlayAnimation() } 143 144 /** 145 * Return true to indicate that we should hide [LightRevealScrim] when waking up 146 */ shouldHideLightRevealScrimOnWakeUpnull147 fun shouldHideLightRevealScrimOnWakeUp(): Boolean = 148 animations.any { it.shouldHideScrimOnWakeUp() } 149 150 /** 151 * Return true to override the dozing state of the notifications to fully dozing, 152 * so the notifications are not visible when playing the animation 153 */ overrideNotificationsFullyDozingOnKeyguardnull154 fun overrideNotificationsFullyDozingOnKeyguard(): Boolean = 155 animations.any { it.overrideNotificationsDozeAmount() } 156 157 /** 158 * Return true to hide the notifications footer ('manage'/'clear all' buttons) 159 */ shouldHideNotificationsFooternull160 fun shouldHideNotificationsFooter(): Boolean = 161 animations.any { it.isAnimationPlaying() } 162 163 /** 164 * Return true to clamp screen brightness to 'dimmed' value when devices times out 165 * and goes to sleep 166 */ shouldClampDozeScreenBrightnessnull167 fun shouldClampDozeScreenBrightness(): Boolean = 168 animations.any { it.shouldPlayAnimation() } 169 170 /** 171 * Return true to show AOD icons even when status bar is in 'shade' state (unlocked) 172 */ shouldShowAodIconsWhenShadenull173 fun shouldShowAodIconsWhenShade(): Boolean = 174 animations.any { it.shouldShowAodIconsWhenShade() } 175 176 /** 177 * Return true to allow animation of appearing AOD icons 178 */ shouldAnimateAodIconsnull179 fun shouldAnimateAodIcons(): Boolean = 180 animations.all { it.shouldAnimateAodIcons() } 181 182 /** 183 * Return true to animate doze state change, if returns false dozing will be applied without 184 * animation (sends only 0.0f or 1.0f dozing progress) 185 */ shouldAnimateDozingChangenull186 fun shouldAnimateDozingChange(): Boolean = 187 animations.all { it.shouldAnimateDozingChange() } 188 189 /** 190 * Returns true when moving display state to power save mode should be 191 * delayed for a few seconds. This might be useful to play animations in full quality, 192 * without reducing FPS. 193 */ shouldDelayDisplayDozeTransitionnull194 fun shouldDelayDisplayDozeTransition(): Boolean = 195 animations.any { it.shouldDelayDisplayDozeTransition() } 196 197 /** 198 * Return true to animate large <-> small clock transition 199 */ shouldAnimateClockChangenull200 fun shouldAnimateClockChange(): Boolean = 201 animations.all { it.shouldAnimateClockChange() } 202 } 203 204 interface ScreenOffAnimation { initializenull205 fun initialize( 206 centralSurfaces: CentralSurfaces, 207 shadeViewController: ShadeViewController, 208 lightRevealScrim: LightRevealScrim, 209 ) {} 210 211 /** 212 * Called when started going to sleep, should return true if the animation will be played 213 */ startAnimationnull214 fun startAnimation(): Boolean = false 215 216 fun shouldPlayAnimation(): Boolean = false 217 fun isAnimationPlaying(): Boolean = false 218 219 fun onScrimOpaqueChanged(isOpaque: Boolean) {} onAlwaysOnChangednull220 fun onAlwaysOnChanged(alwaysOn: Boolean) {} 221 shouldAnimateInKeyguardnull222 fun shouldAnimateInKeyguard(): Boolean = false 223 fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run() 224 225 fun shouldDelayKeyguardShow(): Boolean = false 226 fun isKeyguardShowDelayed(): Boolean = false 227 fun isKeyguardHideDelayed(): Boolean = false 228 fun shouldHideScrimOnWakeUp(): Boolean = false 229 fun overrideNotificationsDozeAmount(): Boolean = false 230 fun shouldShowAodIconsWhenShade(): Boolean = false 231 fun shouldDelayDisplayDozeTransition(): Boolean = false 232 fun shouldAnimateAodIcons(): Boolean = true 233 fun shouldAnimateDozingChange(): Boolean = true 234 fun shouldAnimateClockChange(): Boolean = true 235 } 236