• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.DeadObjectException
23 import android.util.Log
24 import android.view.View
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.dagger.qualifiers.Application
27 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
28 import com.android.systemui.keyguard.ui.binder.InWindowLauncherAnimationViewBinder
29 import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel
30 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
31 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
32 import com.android.systemui.shared.system.smartspace.SmartspaceState
33 import javax.inject.Inject
34 import kotlinx.coroutines.CoroutineScope
35 
36 private val TAG = InWindowLauncherUnlockAnimationManager::class.simpleName
37 private const val UNLOCK_ANIMATION_DURATION = 633L
38 private const val UNLOCK_START_DELAY = 100L
39 
40 /**
41  * Handles interactions between System UI and Launcher related to the in-window unlock animation.
42  *
43  * Launcher registers its unlock controller with us here, and we use that to prepare for and start
44  * the unlock animation.
45  */
46 @SysUISingleton
47 class InWindowLauncherUnlockAnimationManager
48 @Inject
49 constructor(
50     val interactor: InWindowLauncherUnlockAnimationInteractor,
51     val viewModel: InWindowLauncherAnimationViewModel,
52     @Application val scope: CoroutineScope,
53 ) : ISysuiUnlockAnimationController.Stub() {
54 
55     /**
56      * The smartspace view on the lockscreen. This is used to perform the shared element animation
57      * between the lockscreen smartspace and the launcher one.
58      */
59     var lockscreenSmartspace: View? = null
60 
61     private var launcherAnimationController: ILauncherUnlockAnimationController? = null
62 
63     /**
64      * Whether we've called [ILauncherUnlockAnimationController.prepareForUnlock], and have *not*
65      * subsequently called [ILauncherUnlockAnimationController.playUnlockAnimation] or
66      * [ILauncherUnlockAnimationController.setUnlockAmount].
67      */
68     private var preparedForUnlock = false
69 
70     /**
71      * Most recent value passed to [ILauncherUnlockAnimationController.setUnlockAmount] during this
72      * unlock.
73      *
74      * Null if we have not set a manual unlock amount, or once [ensureUnlockedOrAnimatingUnlocked]
75      * has been called.
76      */
77     private var manualUnlockAmount: Float? = null
78 
79     /**
80      * Called from Launcher to provide us with the launcher unlock animation controller, which can
81      * be used to start and update the unlock animation in the launcher 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 
197             try {
198                 it.setUnlockAmount(amount, forceIfAnimating)
199             } catch (e: DeadObjectException) {
200                 Log.e(TAG, "DeadObjectException in setUnlockAmount($amount, $forceIfAnimating)", e)
201             }
202         }
203     }
204 }
205