1 /* <lambda>null2 * Copyright (C) 2023 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.keyguard.ui.viewmodel 18 19 import android.util.MathUtils 20 import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor 23 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor 24 import com.android.systemui.keyguard.shared.model.Edge 25 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD 26 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN 27 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow 28 import com.android.systemui.keyguard.ui.StateToValue 29 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition 30 import com.android.systemui.power.domain.interactor.PowerInteractor 31 import com.android.systemui.power.shared.model.WakeSleepReason.FOLD 32 import com.android.systemui.util.kotlin.sample 33 import javax.inject.Inject 34 import kotlin.time.Duration.Companion.milliseconds 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.flatMapLatest 37 import kotlinx.coroutines.flow.transform 38 39 /** 40 * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume. 41 */ 42 @SysUISingleton 43 class LockscreenToAodTransitionViewModel 44 @Inject 45 constructor( 46 deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, 47 private val powerInteractor: PowerInteractor, 48 shadeDependentFlows: ShadeDependentFlows, 49 animationFlow: KeyguardTransitionAnimationFlow, 50 ) : DeviceEntryIconTransition { 51 52 private val transitionAnimation = 53 animationFlow.setup( 54 duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION, 55 edge = Edge.create(from = LOCKSCREEN, to = AOD), 56 ) 57 58 private val transitionAnimationOnFold = 59 animationFlow.setup( 60 duration = FromLockscreenTransitionInteractor.TO_AOD_FOLD_DURATION, 61 edge = Edge.create(from = LOCKSCREEN, to = AOD), 62 ) 63 64 val deviceEntryBackgroundViewAlpha: Flow<Float> = 65 shadeDependentFlows.transitionFlow( 66 flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f), 67 flowWhenShadeIsNotExpanded = 68 transitionAnimation.sharedFlow( 69 duration = 300.milliseconds, 70 onStep = { 1 - it }, 71 onCancel = { 0f }, 72 onFinish = { 0f }, 73 ), 74 ) 75 76 val shortcutsAlpha: Flow<Float> = 77 transitionAnimation.sharedFlow( 78 duration = 250.milliseconds, 79 onStep = { 1 - it }, 80 onFinish = { 0f }, 81 onCancel = { 1f }, 82 ) 83 84 fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { 85 var startAlpha = 1f 86 return transitionAnimation 87 .sharedFlow( 88 duration = 500.milliseconds, 89 onStart = { startAlpha = viewState.alpha() }, 90 onStep = { MathUtils.lerp(startAlpha, 1f, it) }, 91 ) 92 .sample(powerInteractor.detailedWakefulness, ::Pair) 93 .transform { (alpha, wakefulness) -> 94 if (wakefulness.lastSleepReason != FOLD) { 95 emit(alpha) 96 } 97 } 98 } 99 100 val lockscreenAlphaOnFold: Flow<Float> = 101 transitionAnimationOnFold 102 .sharedFlow( 103 startTime = 600.milliseconds, 104 duration = 500.milliseconds, 105 onStep = { it }, 106 ) 107 .sample(powerInteractor.detailedWakefulness, ::Pair) 108 .transform { (alpha, wakefulness) -> 109 if (wakefulness.lastSleepReason == FOLD) { 110 emit(alpha) 111 } 112 } 113 114 val notificationAlphaOnFold: Flow<Float> = 115 transitionAnimationOnFold 116 .sharedFlow( 117 duration = 1100.milliseconds, 118 onStep = { 0f }, 119 onFinish = { 1f }, 120 ) 121 .sample(powerInteractor.detailedWakefulness, ::Pair) 122 .transform { (alpha, wakefulness) -> 123 if (wakefulness.lastSleepReason == FOLD) { 124 emit(alpha) 125 } 126 } 127 128 /** x-translation from the side of the screen for fold animation */ 129 fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> { 130 return transitionAnimationOnFold 131 .sharedFlowWithState( 132 startTime = 600.milliseconds, 133 duration = 500.milliseconds, 134 onStep = { translatePx + it * -translatePx }, 135 onFinish = { 0f }, 136 interpolator = EMPHASIZED_DECELERATE, 137 ) 138 .sample(powerInteractor.detailedWakefulness, ::Pair) 139 .transform { (stateToValue, wakefulness) -> 140 if (wakefulness.lastSleepReason == FOLD) { 141 emit(stateToValue) 142 } 143 } 144 } 145 146 override val deviceEntryParentViewAlpha: Flow<Float> = 147 deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { 148 isUdfpsEnrolledAndEnabled -> 149 if (isUdfpsEnrolledAndEnabled) { 150 shadeDependentFlows.transitionFlow( 151 flowWhenShadeIsExpanded = // fade in 152 transitionAnimation.sharedFlow( 153 duration = 300.milliseconds, 154 onStep = { it }, 155 onCancel = { 1f }, 156 onFinish = { 1f }, 157 ), 158 flowWhenShadeIsNotExpanded = transitionAnimation.immediatelyTransitionTo(1f), 159 ) 160 } else { 161 shadeDependentFlows.transitionFlow( 162 flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f), 163 flowWhenShadeIsNotExpanded = // fade out 164 transitionAnimation.sharedFlow( 165 duration = 200.milliseconds, 166 onStep = { 1f - it }, 167 onCancel = { 0f }, 168 onFinish = { 0f }, 169 ), 170 ) 171 } 172 } 173 } 174