Merge "Add face scanning error animation" into tm-d1-dev

This commit is contained in:
Beverly Tai
2022-06-23 22:43:12 +00:00
committed by Android (Google) Code Review
3 changed files with 161 additions and 44 deletions

View File

@@ -27,12 +27,15 @@ import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.hardware.biometrics.BiometricSourceType
import android.view.View
import androidx.core.graphics.ColorUtils
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.animation.Interpolators
import com.android.systemui.plugins.statusbar.StatusBarStateController
import java.util.concurrent.Executor
/**
* When the face is enrolled, we use this view to show the face scanning animation and the camera
@@ -42,7 +45,8 @@ class FaceScanningOverlay(
context: Context,
pos: Int,
val statusBarStateController: StatusBarStateController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
val mainExecutor: Executor
) : ScreenDecorations.DisplayCutoutView(context, pos) {
private var showScanningAnim = false
private val rimPaint = Paint()
@@ -54,11 +58,26 @@ class FaceScanningOverlay(
com.android.systemui.R.attr.wallpaperTextColorAccent)
private var cameraProtectionAnimator: ValueAnimator? = null
var hideOverlayRunnable: Runnable? = null
var faceAuthSucceeded = false
init {
visibility = View.INVISIBLE // only show this view when face scanning is happening
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
mainExecutor.execute {
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
mainExecutor.execute {
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
}
}
override fun setColor(color: Int) {
cameraProtectionColor = color
invalidate()
@@ -108,7 +127,6 @@ class FaceScanningOverlay(
if (showScanningAnimNow == showScanningAnim) {
return
}
showScanningAnim = showScanningAnimNow
updateProtectionBoundingPath()
// Delay the relayout until the end of the animation when hiding,
@@ -120,13 +138,20 @@ class FaceScanningOverlay(
cameraProtectionAnimator?.cancel()
cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
if (showScanningAnimNow) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
startDelay = if (showScanningAnim) 0 else PULSE_DISAPPEAR_DURATION
duration = if (showScanningAnim) PULSE_APPEAR_DURATION else
CAMERA_PROTECTION_DISAPPEAR_DURATION
interpolator = if (showScanningAnim) Interpolators.STANDARD else
Interpolators.EMPHASIZED
if (showScanningAnimNow) SHOW_CAMERA_PROTECTION_SCALE
else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
startDelay =
if (showScanningAnim) 0
else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
else PULSE_ERROR_DISAPPEAR_DURATION
duration =
if (showScanningAnim) CAMERA_PROTECTION_APPEAR_DURATION
else if (faceAuthSucceeded) CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION
else CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION
interpolator =
if (showScanningAnim) Interpolators.STANDARD_ACCELERATE
else if (faceAuthSucceeded) Interpolators.STANDARD
else Interpolators.STANDARD_DECELERATE
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
animation: ValueAnimator ->
cameraProtectionProgress = animation.animatedValue as Float
@@ -143,47 +168,73 @@ class FaceScanningOverlay(
}
}
})
start()
}
rimAnimator?.cancel()
rimAnimator = AnimatorSet().apply {
val rimAppearOrDisappearAnimator = ValueAnimator.ofFloat(rimProgress,
if (showScanningAnim) PULSE_RADIUS_OUT else (PULSE_RADIUS_IN * 1.15f)).apply {
duration = if (showScanningAnim) PULSE_APPEAR_DURATION else PULSE_DISAPPEAR_DURATION
interpolator = Interpolators.STANDARD
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
animation: ValueAnimator ->
rimProgress = animation.animatedValue as Float
invalidate()
})
}
if (showScanningAnim) {
// appear and then pulse in/out
playSequentially(rimAppearOrDisappearAnimator,
val rimAppearAnimator = ValueAnimator.ofFloat(SHOW_CAMERA_PROTECTION_SCALE,
PULSE_RADIUS_OUT).apply {
duration = PULSE_APPEAR_DURATION
interpolator = Interpolators.STANDARD_DECELERATE
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
animation: ValueAnimator ->
rimProgress = animation.animatedValue as Float
invalidate()
})
}
// animate in camera protection, rim, and then pulse in/out
playSequentially(cameraProtectionAnimator, rimAppearAnimator,
createPulseAnimator(), createPulseAnimator(),
createPulseAnimator(), createPulseAnimator(),
createPulseAnimator(), createPulseAnimator())
} else {
val opacityAnimator = ValueAnimator.ofInt(255, 0).apply {
duration = PULSE_DISAPPEAR_DURATION
interpolator = Interpolators.LINEAR
val rimDisappearAnimator = ValueAnimator.ofFloat(
rimProgress,
if (faceAuthSucceeded) PULSE_RADIUS_SUCCESS
else SHOW_CAMERA_PROTECTION_SCALE
).apply {
duration =
if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
else PULSE_ERROR_DISAPPEAR_DURATION
interpolator =
if (faceAuthSucceeded) Interpolators.STANDARD_DECELERATE
else Interpolators.STANDARD
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
animation: ValueAnimator ->
rimPaint.alpha = animation.animatedValue as Int
rimProgress = animation.animatedValue as Float
invalidate()
})
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
rimProgress = HIDDEN_RIM_SCALE
invalidate()
}
})
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
rimProgress = HIDDEN_RIM_SCALE
rimPaint.alpha = 255
invalidate()
if (faceAuthSucceeded) {
val successOpacityAnimator = ValueAnimator.ofInt(255, 0).apply {
duration = PULSE_SUCCESS_DISAPPEAR_DURATION
interpolator = Interpolators.LINEAR
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
animation: ValueAnimator ->
rimPaint.alpha = animation.animatedValue as Int
invalidate()
})
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
rimPaint.alpha = 255
invalidate()
}
})
}
})
// disappear
playTogether(rimAppearOrDisappearAnimator, opacityAnimator)
val rimSuccessAnimator = AnimatorSet()
rimSuccessAnimator.playTogether(rimDisappearAnimator, successOpacityAnimator)
playTogether(rimSuccessAnimator, cameraProtectionAnimator)
} else {
playTogether(rimDisappearAnimator, cameraProtectionAnimator)
}
}
addListener(object : AnimatorListenerAdapter() {
@@ -253,15 +304,72 @@ class FaceScanningOverlay(
}
}
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
userId: Int,
biometricSourceType: BiometricSourceType?,
isStrongBiometric: Boolean
) {
if (biometricSourceType == BiometricSourceType.FACE) {
post {
faceAuthSucceeded = true
enableShowProtection(true)
}
}
}
override fun onBiometricAcquired(
biometricSourceType: BiometricSourceType?,
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FACE) {
post {
faceAuthSucceeded = false // reset
}
}
}
override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
if (biometricSourceType == BiometricSourceType.FACE) {
post {
faceAuthSucceeded = false
enableShowProtection(false)
}
}
}
override fun onBiometricError(
msgId: Int,
errString: String?,
biometricSourceType: BiometricSourceType?
) {
if (biometricSourceType == BiometricSourceType.FACE) {
post {
faceAuthSucceeded = false
enableShowProtection(false)
}
}
}
}
companion object {
private const val HIDDEN_RIM_SCALE = HIDDEN_CAMERA_PROTECTION_SCALE
private const val SHOW_CAMERA_PROTECTION_SCALE = 1f
private const val PULSE_RADIUS_IN = 1.1f
private const val PULSE_RADIUS_OUT = 1.125f
private const val PULSE_RADIUS_SUCCESS = 1.25f
private const val CAMERA_PROTECTION_APPEAR_DURATION = 250L
private const val PULSE_APPEAR_DURATION = 250L // without start delay
private const val PULSE_APPEAR_DURATION = 350L
private const val PULSE_DURATION_INWARDS = 500L
private const val PULSE_DURATION_OUTWARDS = 500L
private const val PULSE_DISAPPEAR_DURATION = 850L
private const val CAMERA_PROTECTION_DISAPPEAR_DURATION = 700L // excluding start delay
private const val PULSE_RADIUS_IN = 1.15f
private const val PULSE_RADIUS_OUT = 1.25f
private const val PULSE_SUCCESS_DISAPPEAR_DURATION = 400L
private const val CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION = 500L // without start delay
private const val PULSE_ERROR_DISAPPEAR_DURATION = 200L
private const val CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION = 300L // without start delay
}
}

View File

@@ -32,10 +32,12 @@ import android.widget.FrameLayout
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.FaceScanningOverlay
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import java.util.concurrent.Executor
import javax.inject.Inject
@SysUISingleton
@@ -44,6 +46,7 @@ class FaceScanningProviderFactory @Inject constructor(
private val context: Context,
private val statusBarStateController: StatusBarStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Main private val mainExecutor: Executor,
private val featureFlags: FeatureFlags
) : DecorProviderFactory() {
private val display = context.display
@@ -82,7 +85,9 @@ class FaceScanningProviderFactory @Inject constructor(
bound.baseOnRotation0(displayInfo.rotation),
authController,
statusBarStateController,
keyguardUpdateMonitor)
keyguardUpdateMonitor,
mainExecutor
)
)
}
}
@@ -102,7 +107,8 @@ class FaceScanningOverlayProviderImpl(
override val alignedBound: Int,
private val authController: AuthController,
private val statusBarStateController: StatusBarStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val mainExecutor: Executor
) : BoundDecorProvider() {
override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
@@ -127,7 +133,9 @@ class FaceScanningOverlayProviderImpl(
context,
alignedBound,
statusBarStateController,
keyguardUpdateMonitor)
keyguardUpdateMonitor,
mainExecutor
)
view.id = viewId
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT).let {

View File

@@ -210,7 +210,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
BOUNDS_POSITION_TOP,
mAuthController,
mStatusBarStateController,
mKeyguardUpdateMonitor));
mKeyguardUpdateMonitor,
mExecutor));
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,