• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.biometrics
18 
19 import android.annotation.RawRes
20 import android.content.Context
21 import android.content.Context.FINGERPRINT_SERVICE
22 import android.content.res.Configuration
23 import android.hardware.fingerprint.FingerprintManager
24 import android.view.DisplayInfo
25 import android.view.Surface
26 import android.view.View
27 import com.airbnb.lottie.LottieAnimationView
28 import com.android.settingslib.widget.LottieColorUtils
29 import com.android.systemui.R
30 import com.android.systemui.biometrics.AuthBiometricView.BiometricState
31 import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
32 import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING
33 import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN
34 import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
35 import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
36 import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
37 import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
38 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
39 import com.android.systemui.unfold.updates.FoldProvider
40 
41 /** Fingerprint only icon animator for BiometricPrompt.  */
42 open class AuthBiometricFingerprintIconController(
43         context: Context,
44         iconView: LottieAnimationView,
45         protected val iconViewOverlay: LottieAnimationView
46 ) : AuthIconController(context, iconView), FoldProvider.FoldCallback {
47 
48     private var isDeviceFolded: Boolean = false
49     private val isSideFps: Boolean
50     private val isReverseDefaultRotation =
51             context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
52     private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
53     var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
54         set(value) {
55             if (field == value) {
56                 return
57             }
58             iconViewOverlay.layoutParams.width = value.first
59             iconViewOverlay.layoutParams.height = value.second
60             iconView.layoutParams.width = value.first
61             iconView.layoutParams.height = value.second
62             field = value
63         }
64 
65     init {
66         iconLayoutParamSize = Pair(context.resources.getDimensionPixelSize(
67                 R.dimen.biometric_dialog_fingerprint_icon_width),
68                 context.resources.getDimensionPixelSize(
69                         R.dimen.biometric_dialog_fingerprint_icon_height))
70         isSideFps =
71             (context.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager?)?.let { fpm ->
72                 fpm.sensorPropertiesInternal.any { it.isAnySidefpsType }
73             } ?: false
74         preloadAssets(context)
75         val displayInfo = DisplayInfo()
76         context.display?.getDisplayInfo(displayInfo)
77         if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) {
78             iconView.rotation = 180f
79         }
80         screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
81         screenSizeFoldProvider.onConfigurationChange(context.resources.configuration)
82     }
83 
84     private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
85         val displayInfo = DisplayInfo()
86         context.display?.getDisplayInfo(displayInfo)
87         val rotation = getRotationFromDefault(displayInfo.rotation)
88         val iconAnimation = getSideFpsAnimationForTransition(rotation)
89         val iconViewOverlayAnimation =
90                 getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
91 
92         if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
93             iconView.setAnimation(iconAnimation)
94             iconViewOverlay.setAnimation(iconViewOverlayAnimation)
95         }
96 
97         val iconContentDescription = getIconContentDescription(newState)
98         if (iconContentDescription != null) {
99             iconView.contentDescription = iconContentDescription
100         }
101 
102         iconView.frame = 0
103         iconViewOverlay.frame = 0
104         if (shouldAnimateSfpsIconViewForTransition(lastState, newState)) {
105             iconView.playAnimation()
106         }
107 
108         if (shouldAnimateIconViewOverlayForTransition(lastState, newState)) {
109             iconViewOverlay.playAnimation()
110         }
111 
112         LottieColorUtils.applyDynamicColors(context, iconView)
113         LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
114     }
115 
116     private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) {
117         val icon = getAnimationForTransition(lastState, newState) ?: return
118 
119         if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
120             iconView.setAnimation(icon)
121         }
122 
123         val iconContentDescription = getIconContentDescription(newState)
124         if (iconContentDescription != null) {
125             iconView.contentDescription = iconContentDescription
126         }
127 
128         iconView.frame = 0
129         if (shouldAnimateIconViewForTransition(lastState, newState)) {
130             iconView.playAnimation()
131         }
132         LottieColorUtils.applyDynamicColors(context, iconView)
133     }
134 
135     override fun onConfigurationChanged(newConfig: Configuration) {
136         screenSizeFoldProvider.onConfigurationChange(newConfig)
137     }
138 
139     override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
140         if (isSideFps) {
141             updateIconSideFps(lastState, newState)
142         } else {
143             iconViewOverlay.visibility = View.GONE
144             updateIconNormal(lastState, newState)
145         }
146     }
147 
148     private fun getIconContentDescription(@BiometricState newState: Int): CharSequence? {
149         val id = when (newState) {
150             STATE_IDLE,
151             STATE_AUTHENTICATING_ANIMATING_IN,
152             STATE_AUTHENTICATING,
153             STATE_PENDING_CONFIRMATION,
154             STATE_AUTHENTICATED -> R.string.security_settings_sfps_enroll_find_sensor_message
155             STATE_ERROR,
156             STATE_HELP -> R.string.biometric_dialog_try_again
157             else -> null
158         }
159         return if (id != null) context.getString(id) else null
160     }
161 
162     protected open fun shouldAnimateIconViewForTransition(
163             @BiometricState oldState: Int,
164             @BiometricState newState: Int
165     ) = when (newState) {
166         STATE_HELP,
167         STATE_ERROR -> true
168         STATE_AUTHENTICATING_ANIMATING_IN,
169         STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
170         STATE_AUTHENTICATED -> true
171         else -> false
172     }
173 
174     private fun shouldAnimateSfpsIconViewForTransition(
175             @BiometricState oldState: Int,
176             @BiometricState newState: Int
177     ) = when (newState) {
178         STATE_HELP,
179         STATE_ERROR -> true
180         STATE_AUTHENTICATING_ANIMATING_IN,
181         STATE_AUTHENTICATING ->
182             oldState == STATE_ERROR || oldState == STATE_HELP || oldState == STATE_IDLE
183         STATE_AUTHENTICATED -> true
184         else -> false
185     }
186 
187     protected open fun shouldAnimateIconViewOverlayForTransition(
188             @BiometricState oldState: Int,
189             @BiometricState newState: Int
190     ) = when (newState) {
191         STATE_HELP,
192         STATE_ERROR -> true
193         STATE_AUTHENTICATING_ANIMATING_IN,
194         STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
195         STATE_AUTHENTICATED -> true
196         else -> false
197     }
198 
199     @RawRes
200     protected open fun getAnimationForTransition(
201             @BiometricState oldState: Int,
202             @BiometricState newState: Int
203     ): Int? {
204         val id = when (newState) {
205             STATE_HELP,
206             STATE_ERROR -> {
207                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
208             }
209             STATE_AUTHENTICATING_ANIMATING_IN,
210             STATE_AUTHENTICATING -> {
211                 if (oldState == STATE_ERROR || oldState == STATE_HELP) {
212                     R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
213                 } else {
214                     R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
215                 }
216             }
217             STATE_AUTHENTICATED -> {
218                 if (oldState == STATE_ERROR || oldState == STATE_HELP) {
219                     R.raw.fingerprint_dialogue_error_to_success_lottie
220                 } else {
221                     R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
222                 }
223             }
224             else -> return null
225         }
226         return if (id != null) return id else null
227     }
228 
229     private fun getRotationFromDefault(rotation: Int): Int =
230             if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
231 
232     @RawRes
233     private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
234         Surface.ROTATION_90 -> if (isDeviceFolded) {
235             R.raw.biometricprompt_folded_base_topleft
236         } else {
237             R.raw.biometricprompt_portrait_base_topleft
238         }
239         Surface.ROTATION_270 -> if (isDeviceFolded) {
240             R.raw.biometricprompt_folded_base_bottomright
241         } else {
242             R.raw.biometricprompt_portrait_base_bottomright
243         }
244         else -> if (isDeviceFolded) {
245             R.raw.biometricprompt_folded_base_default
246         } else {
247             R.raw.biometricprompt_landscape_base
248         }
249     }
250 
251     @RawRes
252     private fun getSideFpsOverlayAnimationForTransition(
253             @BiometricState oldState: Int,
254             @BiometricState newState: Int,
255             rotation: Int
256     ): Int? = when (newState) {
257         STATE_HELP,
258         STATE_ERROR -> {
259             when (rotation) {
260                 Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
261                 Surface.ROTATION_90 ->
262                     R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
263                 Surface.ROTATION_180 ->
264                     R.raw.biometricprompt_fingerprint_to_error_landscape
265                 Surface.ROTATION_270 ->
266                     R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
267                 else -> R.raw.biometricprompt_fingerprint_to_error_landscape
268             }
269         }
270         STATE_AUTHENTICATING_ANIMATING_IN,
271         STATE_AUTHENTICATING -> {
272             if (oldState == STATE_ERROR || oldState == STATE_HELP) {
273                 when (rotation) {
274                     Surface.ROTATION_0 ->
275                         R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
276                     Surface.ROTATION_90 ->
277                         R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
278                     Surface.ROTATION_180 ->
279                         R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
280                     Surface.ROTATION_270 ->
281                         R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
282                     else -> R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
283                 }
284             } else {
285                 when (rotation) {
286                     Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
287                     Surface.ROTATION_90 ->
288                         R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
289                     Surface.ROTATION_180 ->
290                         R.raw.biometricprompt_fingerprint_to_error_landscape
291                     Surface.ROTATION_270 ->
292                         R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
293                     else -> R.raw.biometricprompt_fingerprint_to_error_landscape
294                 }
295             }
296         }
297         STATE_AUTHENTICATED -> {
298             if (oldState == STATE_ERROR || oldState == STATE_HELP) {
299                 when (rotation) {
300                     Surface.ROTATION_0 ->
301                         R.raw.biometricprompt_symbol_error_to_success_landscape
302                     Surface.ROTATION_90 ->
303                         R.raw.biometricprompt_symbol_error_to_success_portrait_topleft
304                     Surface.ROTATION_180 ->
305                         R.raw.biometricprompt_symbol_error_to_success_landscape
306                     Surface.ROTATION_270 ->
307                         R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright
308                     else -> R.raw.biometricprompt_symbol_error_to_success_landscape
309                 }
310             } else {
311                 when (rotation) {
312                     Surface.ROTATION_0 ->
313                         R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
314                     Surface.ROTATION_90 ->
315                         R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
316                     Surface.ROTATION_180 ->
317                         R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
318                     Surface.ROTATION_270 ->
319                         R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
320                     else -> R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
321                 }
322             }
323         }
324         else -> null
325     }
326 
327     private fun preloadAssets(context: Context) {
328         if (isSideFps) {
329             cacheLottieAssetsInContext(
330                 context,
331                 R.raw.biometricprompt_fingerprint_to_error_landscape,
332                 R.raw.biometricprompt_folded_base_bottomright,
333                 R.raw.biometricprompt_folded_base_default,
334                 R.raw.biometricprompt_folded_base_topleft,
335                 R.raw.biometricprompt_landscape_base,
336                 R.raw.biometricprompt_portrait_base_bottomright,
337                 R.raw.biometricprompt_portrait_base_topleft,
338                 R.raw.biometricprompt_symbol_error_to_fingerprint_landscape,
339                 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright,
340                 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft,
341                 R.raw.biometricprompt_symbol_error_to_success_landscape,
342                 R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright,
343                 R.raw.biometricprompt_symbol_error_to_success_portrait_topleft,
344                 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright,
345                 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft,
346                 R.raw.biometricprompt_symbol_fingerprint_to_success_landscape,
347                 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright,
348                 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
349             )
350         } else {
351             cacheLottieAssetsInContext(
352                 context,
353                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
354                 R.raw.fingerprint_dialogue_error_to_success_lottie,
355                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
356                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
357             )
358         }
359     }
360 
361     override fun onFoldUpdated(isFolded: Boolean) {
362         isDeviceFolded = isFolded
363     }
364 }
365