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