1 /*
2  *
<lambda>null3  *  * Copyright (C) 2023 The Android Open Source Project
4  *  *
5  *  * Licensed under the Apache License, Version 2.0 (the "License");
6  *  * you may not use this file except in compliance with the License.
7  *  * You may obtain a copy of the License at
8  *  *
9  *  *      http://www.apache.org/licenses/LICENSE-2.0
10  *  *
11  *  * Unless required by applicable law or agreed to in writing, software
12  *  * distributed under the License is distributed on an "AS IS" BASIS,
13  *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  * See the License for the specific language governing permissions and
15  *  * limitations under the License.
16  *
17  *
18  */
19 package com.android.systemui.keyguard.ui.view
20 
21 import android.graphics.Rect
22 import android.util.Log
23 import android.view.View
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.dagger.qualifiers.Application
26 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
27 import com.android.systemui.keyguard.ui.binder.InWindowLauncherAnimationViewBinder
28 import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel
29 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
30 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
31 import com.android.systemui.shared.system.smartspace.SmartspaceState
32 import javax.inject.Inject
33 import kotlinx.coroutines.CoroutineScope
34 
35 private val TAG = InWindowLauncherUnlockAnimationManager::class.simpleName
36 private const val UNLOCK_ANIMATION_DURATION = 633L
37 private const val UNLOCK_START_DELAY = 100L
38 
39 /**
40  * Handles interactions between System UI and Launcher related to the in-window unlock animation.
41  *
42  * Launcher registers its unlock controller with us here, and we use that to prepare for and start
43  * the unlock animation.
44  */
45 @SysUISingleton
46 class InWindowLauncherUnlockAnimationManager
47 @Inject
48 constructor(
49     val interactor: InWindowLauncherUnlockAnimationInteractor,
50     val viewModel: InWindowLauncherAnimationViewModel,
51     @Application val scope: CoroutineScope,
52 ) : ISysuiUnlockAnimationController.Stub() {
53 
54     /**
55      * The smartspace view on the lockscreen. This is used to perform the shared element animation
56      * between the lockscreen smartspace and the launcher one.
57      */
58     var lockscreenSmartspace: View? = null
59 
60     private var launcherAnimationController: ILauncherUnlockAnimationController? = null
61 
62     /**
63      * Whether we've called [ILauncherUnlockAnimationController.prepareForUnlock], and have *not*
64      * subsequently called [ILauncherUnlockAnimationController.playUnlockAnimation] or
65      * [ILauncherUnlockAnimationController.setUnlockAmount].
66      */
67     private var preparedForUnlock = false
68 
69     /**
70      * Most recent value passed to [ILauncherUnlockAnimationController.setUnlockAmount] during this
71      * unlock.
72      *
73      * Null if we have not set a manual unlock amount, or once [ensureUnlockedOrAnimatingUnlocked]
74      * has been called.
75      */
76     private var manualUnlockAmount: Float? = null
77 
78     /**
79      * Called from [OverviewProxyService] to provide us with the launcher unlock animation
80      * controller, which can be used to start and update the unlock animation in the launcher
81      * process.
82      */
83     override fun setLauncherUnlockController(
84         activityClass: String,
85         launcherController: ILauncherUnlockAnimationController,
86     ) {
87         interactor.setLauncherActivityClass(activityClass)
88         launcherAnimationController = launcherController
89 
90         // Bind once we have a launcher controller.
91         InWindowLauncherAnimationViewBinder.bind(viewModel, this, scope)
92     }
93 
94     /**
95      * Called from the launcher process when their smartspace state updates something we should know
96      * about.
97      */
98     override fun onLauncherSmartspaceStateUpdated(state: SmartspaceState?) {
99         interactor.setLauncherSmartspaceState(state)
100     }
101 
102     /**
103      * Requests that the launcher prepare for unlock by becoming blank and optionally positioning
104      * its smartspace at the same position as the lockscreen smartspace.
105      *
106      * This state is dangerous - the launcher will remain blank until we ask it to animate unlocked,
107      * either via [playUnlockAnimation] or [setUnlockAmount]. If you don't want to get funny but bad
108      * bugs titled "tiny launcher" or "Expected: launcher icons; Actual: no icons ever", be very
109      * careful here.
110      */
111     fun prepareForUnlock() {
112         launcherAnimationController?.let { launcher ->
113             if (!preparedForUnlock) {
114                 preparedForUnlock = true
115                 manualUnlockAmount = null
116 
117                 launcher.prepareForUnlock(
118                     false,
119                     Rect(),
120                     0
121                 ) // TODO(b/293894758): Add smartspace animation support.
122             }
123         }
124     }
125 
126     /** Ensures that the launcher is either fully visible, or animating to be fully visible. */
127     fun ensureUnlockedOrAnimatingUnlocked() {
128         val preparedButDidNotStartAnimation =
129             preparedForUnlock && !interactor.startedUnlockAnimation.value
130         val manualUnlockSetButNotFullyVisible =
131             manualUnlockAmount != null && manualUnlockAmount != 1f
132 
133         if (preparedButDidNotStartAnimation) {
134             Log.e(
135                 TAG,
136                 "Called prepareForUnlock(), but not playUnlockAnimation(). " +
137                     "Failing-safe by calling setUnlockAmount(1f)"
138             )
139             setUnlockAmount(1f, forceIfAnimating = true)
140         } else if (manualUnlockSetButNotFullyVisible) {
141             Log.e(
142                 TAG,
143                 "Unlock has ended, but manual unlock amount != 1f. " +
144                     "Failing-safe by calling setUnlockAmount(1f)"
145             )
146             setUnlockAmount(1f, forceIfAnimating = true)
147         }
148 
149         manualUnlockAmount = null // Un-set the manual unlock amount as we're now visible.
150     }
151 
152     /**
153      * Asks launcher to play the in-window unlock animation with the specified parameters.
154      *
155      * Once this is called, we're no longer [preparedForUnlock] as unlock is underway.
156      */
157     fun playUnlockAnimation(
158         unlocked: Boolean,
159         duration: Long = UNLOCK_ANIMATION_DURATION,
160         startDelay: Long = UNLOCK_START_DELAY,
161     ) {
162         if (preparedForUnlock) {
163             launcherAnimationController?.let { launcher ->
164                 launcher.playUnlockAnimation(unlocked, duration, startDelay)
165                 interactor.setStartedUnlockAnimation(true)
166             }
167         } else {
168             Log.e(TAG, "Attempted to call playUnlockAnimation() before prepareToUnlock().")
169         }
170 
171         preparedForUnlock = false
172     }
173 
174     /**
175      * Clears the played unlock animation flag. Since we don't have access to an onAnimationEnd
176      * event for the launcher animation (since it's in a different process), this is called whenever
177      * the transition to GONE ends or the surface becomes unavailable. In both cases, we'd need to
178      * play the animation next time we unlock.
179      */
180     fun clearStartedUnlockAnimation() {
181         interactor.setStartedUnlockAnimation(false)
182     }
183 
184     /**
185      * Manually sets the unlock amount on launcher. This is used to explicitly set us to fully
186      * unlocked, or to manually control the animation (such as during a swipe to unlock).
187      *
188      * Once this is called, we're no longer [preparedForUnlock] since the Launcher icons are not
189      * configured to be invisible for the start of the unlock animation.
190      */
191     fun setUnlockAmount(amount: Float, forceIfAnimating: Boolean) {
192         preparedForUnlock = false
193 
194         launcherAnimationController?.let {
195             manualUnlockAmount = amount
196             it.setUnlockAmount(amount, forceIfAnimating)
197         }
198     }
199 }
200