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 com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE 20 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor 21 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor 22 import com.android.systemui.keyguard.shared.model.Edge 23 import com.android.systemui.keyguard.shared.model.KeyguardState 24 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE 25 import com.android.systemui.keyguard.shared.model.ScrimAlpha 26 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow 27 import com.android.systemui.scene.shared.flag.SceneContainerFlag 28 import com.android.systemui.scene.shared.model.Scenes 29 import com.android.systemui.shade.domain.interactor.ShadeInteractor 30 import com.android.systemui.statusbar.SysuiStatusBarStateController 31 import dagger.Lazy 32 import javax.inject.Inject 33 import kotlin.time.Duration 34 import kotlinx.coroutines.ExperimentalCoroutinesApi 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.distinctUntilChanged 37 import kotlinx.coroutines.flow.flatMapLatest 38 import kotlinx.coroutines.flow.map 39 40 /** ALTERNATE and PRIMARY bouncers common animations */ 41 @OptIn(ExperimentalCoroutinesApi::class) 42 class BouncerToGoneFlows 43 @Inject 44 constructor( 45 private val statusBarStateController: SysuiStatusBarStateController, 46 private val primaryBouncerInteractor: PrimaryBouncerInteractor, 47 private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>, 48 private val shadeInteractor: ShadeInteractor, 49 private val animationFlow: KeyguardTransitionAnimationFlow, 50 ) { 51 /** Common fade for scrim alpha values during *BOUNCER->GONE */ 52 fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> { 53 return if (SceneContainerFlag.isEnabled) { 54 keyguardDismissActionInteractor 55 .get() 56 .willAnimateDismissActionOnLockscreen 57 .flatMapLatest { createScrimAlphaFlow(duration, fromState) { it } } 58 } else { 59 createScrimAlphaFlow( 60 duration, 61 fromState, 62 primaryBouncerInteractor::willRunDismissFromKeyguard 63 ) 64 } 65 } 66 67 /** 68 * When the shade is expanded, make sure that all notifications can be seen immediately during a 69 * transition to GONE. This matters especially when the user has chosen to not show 70 * notifications on the lockscreen and then pulls down the shade, which presents them with an 71 * immediate auth prompt, followed by a notification animation. 72 */ 73 fun showAllNotifications(duration: Duration, from: KeyguardState): Flow<Boolean> { 74 var leaveShadeOpen = false 75 return animationFlow 76 .setup( 77 duration = duration, 78 // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene 79 // transition 80 edge = Edge.create(from = from, to = Scenes.Gone) 81 ) 82 .setupWithoutSceneContainer( 83 edge = Edge.create(from = from, to = GONE), 84 ) 85 .sharedFlow( 86 duration = duration, 87 onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() }, 88 onStep = { if (leaveShadeOpen) 1f else 0f }, 89 onFinish = { 0f }, 90 onCancel = { 0f }, 91 ) 92 .map { it == 1f } 93 .distinctUntilChanged() 94 } 95 96 private fun createScrimAlphaFlow( 97 duration: Duration, 98 fromState: KeyguardState, 99 willRunAnimationOnKeyguard: () -> Boolean 100 ): Flow<ScrimAlpha> { 101 var isShadeExpanded = false 102 var leaveShadeOpen: Boolean = false 103 var willRunDismissFromKeyguard: Boolean = false 104 val transitionAnimation = 105 animationFlow 106 .setup( 107 duration = duration, 108 // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> 109 // scene transition 110 edge = Edge.create(from = fromState, to = Scenes.Gone), 111 ) 112 .setupWithoutSceneContainer( 113 edge = Edge.create(from = fromState, to = GONE), 114 ) 115 116 return shadeInteractor.anyExpansion 117 .map { it > 0f } 118 .distinctUntilChanged() 119 .flatMapLatest { isAnyExpanded -> 120 transitionAnimation 121 .sharedFlow( 122 duration = duration, 123 interpolator = EMPHASIZED_ACCELERATE, 124 onStart = { 125 leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() 126 willRunDismissFromKeyguard = willRunAnimationOnKeyguard() 127 isShadeExpanded = isAnyExpanded 128 }, 129 onStep = { 1f - it }, 130 ) 131 .map { 132 if (willRunDismissFromKeyguard) { 133 if (isShadeExpanded) { 134 ScrimAlpha( 135 behindAlpha = it, 136 notificationsAlpha = it, 137 ) 138 } else { 139 ScrimAlpha() 140 } 141 } else if (leaveShadeOpen) { 142 ScrimAlpha( 143 behindAlpha = 1f, 144 notificationsAlpha = 1f, 145 ) 146 } else { 147 ScrimAlpha(behindAlpha = it) 148 } 149 } 150 } 151 } 152 } 153