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.domain.interactor
18 
19 import android.content.Context
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
22 import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.isSurfaceVisible
23 import com.android.systemui.keyguard.shared.model.KeyguardState
24 import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
25 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
26 import com.android.systemui.util.kotlin.sample
27 import com.android.systemui.util.kotlin.toPx
28 import dagger.Lazy
29 import javax.inject.Inject
30 import kotlinx.coroutines.flow.Flow
31 import kotlinx.coroutines.flow.combine
32 import kotlinx.coroutines.flow.distinctUntilChanged
33 import kotlinx.coroutines.flow.map
34 import kotlinx.coroutines.flow.onStart
35 
36 /**
37  * Distance over which the surface behind the keyguard is animated in during a Y-translation
38  * animation.
39  */
40 const val SURFACE_TRANSLATION_Y_DISTANCE_DP = 250
41 
42 @SysUISingleton
43 class KeyguardSurfaceBehindInteractor
44 @Inject
45 constructor(
46     private val repository: KeyguardSurfaceBehindRepository,
47     context: Context,
48     transitionInteractor: KeyguardTransitionInteractor,
49     inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
50     swipeToDismissInteractor: SwipeToDismissInteractor,
51     notificationLaunchInteractor: NotificationLaunchAnimationInteractor,
52 ) {
53     /**
54      * The view params to use for the surface. These params describe the alpha/translation values to
55      * apply, as well as animation parameters if necessary.
56      */
57     val viewParams: Flow<KeyguardSurfaceBehindModel> =
58         combine(
59                 transitionInteractor.startedKeyguardTransitionStep,
60                 transitionInteractor.currentKeyguardState,
61                 notificationLaunchInteractor.isLaunchAnimationRunning,
62             ) { startedStep, currentState, notifAnimationRunning ->
63                 // If we're in transition to GONE, special unlock animation params apply.
64                 if (startedStep.to == KeyguardState.GONE && currentState != KeyguardState.GONE) {
65                     if (notifAnimationRunning) {
66                         // If the notification launch animation is running, leave the alpha at 0f.
67                         // The ActivityLaunchAnimator will morph it from the notification at the
68                         // appropriate time.
69                         return@combine KeyguardSurfaceBehindModel(
70                             alpha = 0f,
71                         )
72                     } else if (
73                         inWindowLauncherUnlockAnimationInteractor.get().isLauncherUnderneath()
74                     ) {
75                         // The Launcher icons have their own translation/alpha animations during the
76                         // in-window animation. We'll just make the surface visible and let Launcher
77                         // do its thing.
78                         return@combine KeyguardSurfaceBehindModel(
79                             alpha = 1f,
80                         )
81                     } else {
82                         // Otherwise, animate a surface in via alpha/translation, and apply the
83                         // swipe velocity (if available) to the translation spring.
84                         return@combine KeyguardSurfaceBehindModel(
85                             animateFromAlpha = 0f,
86                             alpha = 1f,
87                             animateFromTranslationY =
88                                 SURFACE_TRANSLATION_Y_DISTANCE_DP.toPx(context).toFloat(),
89                             translationY = 0f,
90                             startVelocity = swipeToDismissInteractor.dismissFling.value?.velocity
91                                     ?: 0f,
92                         )
93                     }
94                 }
95 
96                 // Default to the visibility of the current state, with no animations.
97                 KeyguardSurfaceBehindModel(alpha = if (isSurfaceVisible(currentState)) 1f else 0f)
98             }
99             .distinctUntilChanged()
100 
101     /**
102      * Whether a notification launch animation is running when we're not already in the GONE state.
103      */
104     private val isNotificationLaunchAnimationRunningOnKeyguard =
105         notificationLaunchInteractor.isLaunchAnimationRunning
106             .sample(transitionInteractor.finishedKeyguardState, ::Pair)
107             .map { (animationRunning, finishedState) ->
108                 animationRunning && finishedState != KeyguardState.GONE
109             }
110             .onStart { emit(false) }
111 
112     /**
113      * Whether we're animating the surface, or a notification launch animation is running (which
114      * means we're going to animate the surface, even if animators aren't yet running).
115      */
116     val isAnimatingSurface =
117         combine(
118             repository.isAnimatingSurface,
119             isNotificationLaunchAnimationRunningOnKeyguard,
120         ) { animatingSurface, animatingLaunch ->
121             animatingSurface || animatingLaunch
122         }
123 
124     fun setAnimatingSurface(animating: Boolean) {
125         repository.setAnimatingSurface(animating)
126     }
127 
128     fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) {
129         repository.setSurfaceRemoteAnimationTargetAvailable(available)
130     }
131 }
132