1 /* 2 * 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.DrawableRes 20 import android.content.Context 21 import android.content.res.Configuration 22 import android.graphics.drawable.Animatable2 23 import android.graphics.drawable.AnimatedVectorDrawable 24 import android.graphics.drawable.Drawable 25 import android.util.Log 26 import com.airbnb.lottie.LottieAnimationView 27 import com.airbnb.lottie.LottieCompositionFactory 28 import com.android.systemui.biometrics.AuthBiometricView.BiometricState 29 30 private const val TAG = "AuthIconController" 31 32 /** Controller for animating the BiometricPrompt icon/affordance. */ 33 abstract class AuthIconController( 34 protected val context: Context, 35 protected val iconView: LottieAnimationView 36 ) : Animatable2.AnimationCallback() { 37 38 /** If this controller should ignore events and pause. */ 39 var deactivated: Boolean = false 40 41 /** If the icon view should be treated as an alternate "confirm" button. */ 42 open val actsAsConfirmButton: Boolean = false 43 onAnimationStartnull44 final override fun onAnimationStart(drawable: Drawable) { 45 super.onAnimationStart(drawable) 46 } 47 onAnimationEndnull48 final override fun onAnimationEnd(drawable: Drawable) { 49 super.onAnimationEnd(drawable) 50 51 if (!deactivated) { 52 handleAnimationEnd(drawable) 53 } 54 } 55 56 /** Set the icon to a static image. */ showStaticDrawablenull57 protected fun showStaticDrawable(@DrawableRes iconRes: Int) { 58 iconView.setImageDrawable(context.getDrawable(iconRes)) 59 } 60 61 /** Animate a resource. */ animateIconOncenull62 protected fun animateIconOnce(@DrawableRes iconRes: Int) { 63 animateIcon(iconRes, false) 64 } 65 66 /** Animate a resource. */ animateIconnull67 protected fun animateIcon(@DrawableRes iconRes: Int, repeat: Boolean) { 68 if (!deactivated) { 69 val icon = context.getDrawable(iconRes) as AnimatedVectorDrawable 70 iconView.setImageDrawable(icon) 71 icon.forceAnimationOnUI() 72 if (repeat) { 73 icon.registerAnimationCallback(this) 74 } 75 icon.start() 76 } 77 } 78 79 /** Update the icon to reflect the [newState]. */ updateStatenull80 fun updateState(@BiometricState lastState: Int, @BiometricState newState: Int) { 81 if (deactivated) { 82 Log.w(TAG, "Ignoring updateState when deactivated: $newState") 83 } else { 84 updateIcon(lastState, newState) 85 } 86 } 87 88 /** If the icon should act as a "retry" button in the [currentState]. */ iconTapSendsRetryWhennull89 fun iconTapSendsRetryWhen(@BiometricState currentState: Int): Boolean = false 90 91 /** Call during [updateState] if the controller is not [deactivated]. */ 92 abstract fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) 93 94 /** Called during [onAnimationEnd] if the controller is not [deactivated]. */ 95 open fun handleAnimationEnd(drawable: Drawable) {} 96 onConfigurationChangednull97 open fun onConfigurationChanged(newConfig: Configuration) {} 98 99 // TODO(b/251476085): Migrate this to an extension at the appropriate level? 100 /** Load the given [rawResources] immediately so they are cached for use in the [context]. */ cacheLottieAssetsInContextnull101 protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) { 102 for (res in rawResources) { 103 LottieCompositionFactory.fromRawRes(context, res) 104 } 105 } 106 } 107