Merge "Add face scanning error animation" into tm-d1-dev am: efcb352fe2
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18914635 Change-Id: I9a9925fdce81f3612762b3468ea66ecb457841c9 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -27,12 +27,15 @@ import android.graphics.Matrix
|
|||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.Path
|
import android.graphics.Path
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.hardware.biometrics.BiometricSourceType
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import com.android.keyguard.KeyguardUpdateMonitor
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||||
import com.android.settingslib.Utils
|
import com.android.settingslib.Utils
|
||||||
import com.android.systemui.animation.Interpolators
|
import com.android.systemui.animation.Interpolators
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
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
|
* 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,
|
context: Context,
|
||||||
pos: Int,
|
pos: Int,
|
||||||
val statusBarStateController: StatusBarStateController,
|
val statusBarStateController: StatusBarStateController,
|
||||||
val keyguardUpdateMonitor: KeyguardUpdateMonitor
|
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||||
|
val mainExecutor: Executor
|
||||||
) : ScreenDecorations.DisplayCutoutView(context, pos) {
|
) : ScreenDecorations.DisplayCutoutView(context, pos) {
|
||||||
private var showScanningAnim = false
|
private var showScanningAnim = false
|
||||||
private val rimPaint = Paint()
|
private val rimPaint = Paint()
|
||||||
@@ -54,11 +58,26 @@ class FaceScanningOverlay(
|
|||||||
com.android.systemui.R.attr.wallpaperTextColorAccent)
|
com.android.systemui.R.attr.wallpaperTextColorAccent)
|
||||||
private var cameraProtectionAnimator: ValueAnimator? = null
|
private var cameraProtectionAnimator: ValueAnimator? = null
|
||||||
var hideOverlayRunnable: Runnable? = null
|
var hideOverlayRunnable: Runnable? = null
|
||||||
|
var faceAuthSucceeded = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
visibility = View.INVISIBLE // only show this view when face scanning is happening
|
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) {
|
override fun setColor(color: Int) {
|
||||||
cameraProtectionColor = color
|
cameraProtectionColor = color
|
||||||
invalidate()
|
invalidate()
|
||||||
@@ -108,7 +127,6 @@ class FaceScanningOverlay(
|
|||||||
if (showScanningAnimNow == showScanningAnim) {
|
if (showScanningAnimNow == showScanningAnim) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
showScanningAnim = showScanningAnimNow
|
showScanningAnim = showScanningAnimNow
|
||||||
updateProtectionBoundingPath()
|
updateProtectionBoundingPath()
|
||||||
// Delay the relayout until the end of the animation when hiding,
|
// Delay the relayout until the end of the animation when hiding,
|
||||||
@@ -120,13 +138,20 @@ class FaceScanningOverlay(
|
|||||||
|
|
||||||
cameraProtectionAnimator?.cancel()
|
cameraProtectionAnimator?.cancel()
|
||||||
cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
|
cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
|
||||||
if (showScanningAnimNow) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
|
if (showScanningAnimNow) SHOW_CAMERA_PROTECTION_SCALE
|
||||||
startDelay = if (showScanningAnim) 0 else PULSE_DISAPPEAR_DURATION
|
else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
|
||||||
duration = if (showScanningAnim) PULSE_APPEAR_DURATION else
|
startDelay =
|
||||||
CAMERA_PROTECTION_DISAPPEAR_DURATION
|
if (showScanningAnim) 0
|
||||||
interpolator = if (showScanningAnim) Interpolators.STANDARD else
|
else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
|
||||||
Interpolators.EMPHASIZED
|
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 {
|
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
|
||||||
animation: ValueAnimator ->
|
animation: ValueAnimator ->
|
||||||
cameraProtectionProgress = animation.animatedValue as Float
|
cameraProtectionProgress = animation.animatedValue as Float
|
||||||
@@ -143,47 +168,73 @@ class FaceScanningOverlay(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rimAnimator?.cancel()
|
rimAnimator?.cancel()
|
||||||
rimAnimator = AnimatorSet().apply {
|
rimAnimator = AnimatorSet().apply {
|
||||||
val rimAppearOrDisappearAnimator = ValueAnimator.ofFloat(rimProgress,
|
if (showScanningAnim) {
|
||||||
if (showScanningAnim) PULSE_RADIUS_OUT else (PULSE_RADIUS_IN * 1.15f)).apply {
|
val rimAppearAnimator = ValueAnimator.ofFloat(SHOW_CAMERA_PROTECTION_SCALE,
|
||||||
duration = if (showScanningAnim) PULSE_APPEAR_DURATION else PULSE_DISAPPEAR_DURATION
|
PULSE_RADIUS_OUT).apply {
|
||||||
interpolator = Interpolators.STANDARD
|
duration = PULSE_APPEAR_DURATION
|
||||||
|
interpolator = Interpolators.STANDARD_DECELERATE
|
||||||
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
|
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
|
||||||
animation: ValueAnimator ->
|
animation: ValueAnimator ->
|
||||||
rimProgress = animation.animatedValue as Float
|
rimProgress = animation.animatedValue as Float
|
||||||
invalidate()
|
invalidate()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (showScanningAnim) {
|
|
||||||
// appear and then pulse in/out
|
// animate in camera protection, rim, and then pulse in/out
|
||||||
playSequentially(rimAppearOrDisappearAnimator,
|
playSequentially(cameraProtectionAnimator, rimAppearAnimator,
|
||||||
createPulseAnimator(), createPulseAnimator(),
|
createPulseAnimator(), createPulseAnimator(),
|
||||||
createPulseAnimator(), createPulseAnimator(),
|
createPulseAnimator(), createPulseAnimator(),
|
||||||
createPulseAnimator(), createPulseAnimator())
|
createPulseAnimator(), createPulseAnimator())
|
||||||
} else {
|
} else {
|
||||||
val opacityAnimator = ValueAnimator.ofInt(255, 0).apply {
|
val rimDisappearAnimator = ValueAnimator.ofFloat(
|
||||||
duration = PULSE_DISAPPEAR_DURATION
|
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 ->
|
||||||
|
rimProgress = animation.animatedValue as Float
|
||||||
|
invalidate()
|
||||||
|
})
|
||||||
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
|
rimProgress = HIDDEN_RIM_SCALE
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (faceAuthSucceeded) {
|
||||||
|
val successOpacityAnimator = ValueAnimator.ofInt(255, 0).apply {
|
||||||
|
duration = PULSE_SUCCESS_DISAPPEAR_DURATION
|
||||||
interpolator = Interpolators.LINEAR
|
interpolator = Interpolators.LINEAR
|
||||||
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
|
addUpdateListener(ValueAnimator.AnimatorUpdateListener {
|
||||||
animation: ValueAnimator ->
|
animation: ValueAnimator ->
|
||||||
rimPaint.alpha = animation.animatedValue as Int
|
rimPaint.alpha = animation.animatedValue as Int
|
||||||
invalidate()
|
invalidate()
|
||||||
})
|
})
|
||||||
}
|
|
||||||
addListener(object : AnimatorListenerAdapter() {
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
rimProgress = HIDDEN_RIM_SCALE
|
|
||||||
rimPaint.alpha = 255
|
rimPaint.alpha = 255
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
// disappear
|
val rimSuccessAnimator = AnimatorSet()
|
||||||
playTogether(rimAppearOrDisappearAnimator, opacityAnimator)
|
rimSuccessAnimator.playTogether(rimDisappearAnimator, successOpacityAnimator)
|
||||||
|
playTogether(rimSuccessAnimator, cameraProtectionAnimator)
|
||||||
|
} else {
|
||||||
|
playTogether(rimDisappearAnimator, cameraProtectionAnimator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addListener(object : AnimatorListenerAdapter() {
|
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 {
|
companion object {
|
||||||
private const val HIDDEN_RIM_SCALE = HIDDEN_CAMERA_PROTECTION_SCALE
|
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_INWARDS = 500L
|
||||||
private const val PULSE_DURATION_OUTWARDS = 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_SUCCESS_DISAPPEAR_DURATION = 400L
|
||||||
private const val PULSE_RADIUS_IN = 1.15f
|
private const val CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION = 500L // without start delay
|
||||||
private const val PULSE_RADIUS_OUT = 1.25f
|
|
||||||
|
private const val PULSE_ERROR_DISAPPEAR_DURATION = 200L
|
||||||
|
private const val CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION = 300L // without start delay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ import android.widget.FrameLayout
|
|||||||
import com.android.keyguard.KeyguardUpdateMonitor
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
import com.android.systemui.FaceScanningOverlay
|
import com.android.systemui.FaceScanningOverlay
|
||||||
import com.android.systemui.biometrics.AuthController
|
import com.android.systemui.biometrics.AuthController
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.flags.FeatureFlags
|
import com.android.systemui.flags.FeatureFlags
|
||||||
import com.android.systemui.flags.Flags
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
|
import java.util.concurrent.Executor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@@ -44,6 +46,7 @@ class FaceScanningProviderFactory @Inject constructor(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val statusBarStateController: StatusBarStateController,
|
private val statusBarStateController: StatusBarStateController,
|
||||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||||
|
@Main private val mainExecutor: Executor,
|
||||||
private val featureFlags: FeatureFlags
|
private val featureFlags: FeatureFlags
|
||||||
) : DecorProviderFactory() {
|
) : DecorProviderFactory() {
|
||||||
private val display = context.display
|
private val display = context.display
|
||||||
@@ -82,7 +85,9 @@ class FaceScanningProviderFactory @Inject constructor(
|
|||||||
bound.baseOnRotation0(displayInfo.rotation),
|
bound.baseOnRotation0(displayInfo.rotation),
|
||||||
authController,
|
authController,
|
||||||
statusBarStateController,
|
statusBarStateController,
|
||||||
keyguardUpdateMonitor)
|
keyguardUpdateMonitor,
|
||||||
|
mainExecutor
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +107,8 @@ class FaceScanningOverlayProviderImpl(
|
|||||||
override val alignedBound: Int,
|
override val alignedBound: Int,
|
||||||
private val authController: AuthController,
|
private val authController: AuthController,
|
||||||
private val statusBarStateController: StatusBarStateController,
|
private val statusBarStateController: StatusBarStateController,
|
||||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor
|
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||||
|
private val mainExecutor: Executor
|
||||||
) : BoundDecorProvider() {
|
) : BoundDecorProvider() {
|
||||||
override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
|
override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
|
||||||
|
|
||||||
@@ -127,7 +133,9 @@ class FaceScanningOverlayProviderImpl(
|
|||||||
context,
|
context,
|
||||||
alignedBound,
|
alignedBound,
|
||||||
statusBarStateController,
|
statusBarStateController,
|
||||||
keyguardUpdateMonitor)
|
keyguardUpdateMonitor,
|
||||||
|
mainExecutor
|
||||||
|
)
|
||||||
view.id = viewId
|
view.id = viewId
|
||||||
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT).let {
|
ViewGroup.LayoutParams.MATCH_PARENT).let {
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
|||||||
BOUNDS_POSITION_TOP,
|
BOUNDS_POSITION_TOP,
|
||||||
mAuthController,
|
mAuthController,
|
||||||
mStatusBarStateController,
|
mStatusBarStateController,
|
||||||
mKeyguardUpdateMonitor));
|
mKeyguardUpdateMonitor,
|
||||||
|
mExecutor));
|
||||||
|
|
||||||
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
|
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
|
||||||
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
|
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
|
||||||
|
|||||||
Reference in New Issue
Block a user