1 /*
<lambda>null2  * Copyright (C) 2022 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_DECELERATE
20 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
23 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
24 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
25 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
26 import com.android.systemui.keyguard.shared.model.Edge
27 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
28 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
29 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
30 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
31 import com.android.systemui.res.R
32 import com.android.systemui.util.kotlin.pairwise
33 import javax.inject.Inject
34 import kotlin.time.Duration.Companion.milliseconds
35 import kotlinx.coroutines.ExperimentalCoroutinesApi
36 import kotlinx.coroutines.flow.Flow
37 import kotlinx.coroutines.flow.filter
38 import kotlinx.coroutines.flow.flatMapLatest
39 import kotlinx.coroutines.flow.map
40 import kotlinx.coroutines.flow.merge
41 
42 /**
43  * Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
44  * consume.
45  */
46 @ExperimentalCoroutinesApi
47 @SysUISingleton
48 class OccludedToLockscreenTransitionViewModel
49 @Inject
50 constructor(
51     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
52     configurationInteractor: ConfigurationInteractor,
53     animationFlow: KeyguardTransitionAnimationFlow,
54     keyguardInteractor: KeyguardInteractor,
55     keyguardTransitionInteractor: KeyguardTransitionInteractor,
56 ) : DeviceEntryIconTransition {
57 
58     private val transitionAnimation =
59         animationFlow.setup(
60             duration = TO_LOCKSCREEN_DURATION,
61             edge = Edge.create(from = OCCLUDED, to = LOCKSCREEN),
62         )
63 
64     /** Lockscreen views y-translation */
65     val lockscreenTranslationY: Flow<Float> =
66         configurationInteractor
67             .dimensionPixelSize(R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y)
68             .flatMapLatest { translatePx ->
69                 transitionAnimation.sharedFlow(
70                     duration = TO_LOCKSCREEN_DURATION,
71                     onStep = { value -> -translatePx + value * translatePx },
72                     interpolator = EMPHASIZED_DECELERATE,
73                     onCancel = { 0f },
74                 )
75             }
76 
77     val shortcutsAlpha: Flow<Float> =
78         transitionAnimation.sharedFlow(
79             duration = 250.milliseconds,
80             onStep = { it },
81             onCancel = { 0f },
82         )
83 
84     /** Lockscreen views alpha */
85     val lockscreenAlpha: Flow<Float> =
86         merge(
87             transitionAnimation.sharedFlow(
88                 startTime = 233.milliseconds,
89                 duration = 250.milliseconds,
90                 onStep = { it },
91                 name = "OCCLUDED->LOCKSCREEN: lockscreenAlpha",
92             ),
93             // Required to fix a bug where the shade expands while lockscreenAlpha=1f, due to a call
94             // to setOccluded(false) triggering a reset() call in KeyguardViewMediator. The
95             // permanent solution is to only expand the shade once the keyguard transition from
96             // OCCLUDED starts, but that requires more refactoring of expansion amounts. For now,
97             // emit alpha = 0f for OCCLUDED -> LOCKSCREEN whenever isOccluded flips from true to
98             // false while currentState == OCCLUDED, so that alpha = 0f when that expansion occurs.
99             // TODO(b/332946323): Remove this once it's no longer needed.
100             keyguardInteractor.isKeyguardOccluded
101                 .pairwise()
102                 .filter { (wasOccluded, isOccluded) ->
103                     wasOccluded &&
104                         !isOccluded &&
105                         keyguardTransitionInteractor.getCurrentState() == OCCLUDED
106                 }
107                 .map { 0f }
108         )
109 
110     val deviceEntryBackgroundViewAlpha: Flow<Float> =
111         transitionAnimation.immediatelyTransitionTo(1f)
112 
113     override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
114 }
115