diff options
| author | 2023-09-13 17:43:22 +0000 | |
|---|---|---|
| committer | 2023-09-13 17:43:22 +0000 | |
| commit | d9c84d54e761ac950d7e22e589a7aa02c5ae5ba8 (patch) | |
| tree | 4ee55dca1f71f2d8924dc27e8a258aba3dedd076 | |
| parent | c79159b4dace114dbac0051c3edacadf7080a901 (diff) | |
| parent | 55e60b12a3e3fceb1490b32baca3d4072d23e274 (diff) | |
Merge "Remove released flag BIOMETRIC_BP_STRONG." into main
30 files changed, 236 insertions, 2534 deletions
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 000612b1fb6f..77925d6f03cd 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -296,8 +296,6 @@ filegroup { /* Biometric converted tests */ "tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt", - "tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt", - "tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt", "tests/src/com/android/systemui/biometrics/AuthControllerTest.java", "tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java", "tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt", diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml deleted file mode 100644 index 50b3bec93731..000000000000 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ /dev/null @@ -1,165 +0,0 @@ -<!-- - ~ Copyright (C) 2019 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- TODO(b/251476085): inline in biometric_prompt_layout after Biometric*Views are un-flagged --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="@integer/biometric_dialog_text_gravity" - android:singleLine="true" - android:marqueeRepeatLimit="1" - android:ellipsize="marquee" - android:importantForAccessibility="no" - style="@style/TextAppearance.AuthCredential.Title"/> - - <TextView - android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="@integer/biometric_dialog_text_gravity" - android:singleLine="true" - android:marqueeRepeatLimit="1" - android:ellipsize="marquee" - android:importantForAccessibility="no" - style="@style/TextAppearance.AuthCredential.Subtitle"/> - - <TextView - android:id="@+id/description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollbars ="vertical" - style="@style/TextAppearance.AuthCredential.Description"/> - - <Space android:id="@+id/space_above_icon" - android:layout_width="match_parent" - android:layout_height="48dp" /> - - <FrameLayout - android:id="@+id/biometric_icon_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center"> - - <include layout="@layout/auth_biometric_icon"/> - - <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper - android:id="@+id/biometric_icon_overlay" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:contentDescription="@null" - android:scaleType="fitXY" /> - </FrameLayout> - - <!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra - padding so that the biometric icon is always in the right physical position. --> - <Space android:id="@+id/space_below_icon" - android:layout_width="match_parent" - android:layout_height="12dp" /> - - <TextView - android:id="@+id/indicator" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingHorizontal="24dp" - android:textSize="12sp" - android:gravity="center_horizontal" - android:accessibilityLiveRegion="polite" - android:singleLine="true" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:scrollHorizontally="true" - android:fadingEdge="horizontal" - android:textColor="@color/biometric_dialog_gray"/> - - <LinearLayout - android:id="@+id/button_bar" - android:layout_width="match_parent" - android:layout_height="88dp" - style="?android:attr/buttonBarStyle" - android:orientation="horizontal" - android:paddingTop="24dp"> - - <Space android:id="@+id/leftSpacer" - android:layout_width="8dp" - android:layout_height="match_parent" - android:visibility="visible" /> - - <!-- Negative Button, reserved for app --> - <Button android:id="@+id/button_negative" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:layout_gravity="center_vertical" - android:ellipsize="end" - android:maxLines="2" - android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"/> - <!-- Cancel Button, replaces negative button when biometric is accepted --> - <Button android:id="@+id/button_cancel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:layout_gravity="center_vertical" - android:maxWidth="@dimen/biometric_dialog_button_negative_max_width" - android:text="@string/cancel" - android:visibility="gone"/> - <!-- "Use Credential" Button, replaces if device credential is allowed --> - <Button android:id="@+id/button_use_credential" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" - android:layout_gravity="center_vertical" - android:maxWidth="@dimen/biometric_dialog_button_negative_max_width" - android:visibility="gone"/> - - <Space android:id="@+id/middleSpacer" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:visibility="visible"/> - - <!-- Positive Button --> - <Button android:id="@+id/button_confirm" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:layout_gravity="center_vertical" - android:ellipsize="end" - android:maxLines="2" - android:maxWidth="@dimen/biometric_dialog_button_positive_max_width" - android:text="@string/biometric_dialog_confirm" - android:visibility="gone"/> - <!-- Try Again Button --> - <Button android:id="@+id/button_try_again" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@*android:style/Widget.DeviceDefault.Button.Colored" - android:layout_gravity="center_vertical" - android:ellipsize="end" - android:maxLines="2" - android:maxWidth="@dimen/biometric_dialog_button_positive_max_width" - android:text="@string/biometric_dialog_try_again" - android:visibility="gone"/> - - <Space android:id="@+id/rightSpacer" - android:layout_width="8dp" - android:layout_height="match_parent" - android:visibility="visible" /> - </LinearLayout> - -</merge>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_biometric_face_view.xml b/packages/SystemUI/res/layout/auth_biometric_face_view.xml deleted file mode 100644 index e3d073276369..000000000000 --- a/packages/SystemUI/res/layout/auth_biometric_face_view.xml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- - ~ Copyright (C) 2019 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- TODO(b/251476085): remove after BiometricFaceView is un-flagged --> -<com.android.systemui.biometrics.AuthBiometricFaceView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/contents" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <include layout="@layout/auth_biometric_contents"/> - -</com.android.systemui.biometrics.AuthBiometricFaceView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml b/packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml deleted file mode 100644 index 896d8362f5b9..000000000000 --- a/packages/SystemUI/res/layout/auth_biometric_fingerprint_and_face_view.xml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- TODO(b/251476085): remove after BiometricFingerprintAndFaceView is un-flagged --> -<com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/contents" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <include layout="@layout/auth_biometric_contents"/> - -</com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml b/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml deleted file mode 100644 index e36f9796be47..000000000000 --- a/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- - ~ Copyright (C) 2019 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- TODO(b/251476085): remove after BiometricFingerprintView is un-flagged --> -<com.android.systemui.biometrics.AuthBiometricFingerprintView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/contents" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <include layout="@layout/auth_biometric_contents"/> - -</com.android.systemui.biometrics.AuthBiometricFingerprintView>
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt index 0c7d56f46530..ea8f5d376ba7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt @@ -20,14 +20,7 @@ import android.graphics.drawable.Drawable import android.util.Log import com.airbnb.lottie.LottieAnimationView import com.android.systemui.R -import com.android.systemui.biometrics.AuthBiometricView.BiometricState -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN -import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR -import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP -import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE -import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState private const val TAG = "AuthBiometricFaceIconController" @@ -40,8 +33,7 @@ class AuthBiometricFaceIconController( // false = dark to light, true = light to dark private var lastPulseLightToDark = false - @BiometricState - private var state = 0 + private var state: BiometricState = BiometricState.STATE_IDLE init { val size = context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size) @@ -66,54 +58,54 @@ class AuthBiometricFaceIconController( } override fun handleAnimationEnd(drawable: Drawable) { - if (state == STATE_AUTHENTICATING || state == STATE_HELP) { + if (state == BiometricState.STATE_AUTHENTICATING || state == BiometricState.STATE_HELP) { pulseInNextDirection() } } - override fun updateIcon(@BiometricState oldState: Int, @BiometricState newState: Int) { - val lastStateIsErrorIcon = (oldState == STATE_ERROR || oldState == STATE_HELP) - if (newState == STATE_AUTHENTICATING_ANIMATING_IN) { + override fun updateIcon(oldState: BiometricState, newState: BiometricState) { + val lastStateIsErrorIcon = (oldState == BiometricState.STATE_ERROR || oldState == BiometricState.STATE_HELP) + if (newState == BiometricState.STATE_AUTHENTICATING_ANIMATING_IN) { showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_authenticating ) - } else if (newState == STATE_AUTHENTICATING) { + } else if (newState == BiometricState.STATE_AUTHENTICATING) { startPulsing() iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_authenticating ) - } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) { + } else if (oldState == BiometricState.STATE_PENDING_CONFIRMATION && newState == BiometricState.STATE_AUTHENTICATED) { animateIconOnce(R.drawable.face_dialog_dark_to_checkmark) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_confirmed ) - } else if (lastStateIsErrorIcon && newState == STATE_IDLE) { + } else if (lastStateIsErrorIcon && newState == BiometricState.STATE_IDLE) { animateIconOnce(R.drawable.face_dialog_error_to_idle) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_idle ) - } else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) { + } else if (lastStateIsErrorIcon && newState == BiometricState.STATE_AUTHENTICATED) { animateIconOnce(R.drawable.face_dialog_dark_to_checkmark) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_authenticated ) - } else if (newState == STATE_ERROR && oldState != STATE_ERROR) { + } else if (newState == BiometricState.STATE_ERROR && oldState != BiometricState.STATE_ERROR) { animateIconOnce(R.drawable.face_dialog_dark_to_error) iconView.contentDescription = context.getString( R.string.keyguard_face_failed ) - } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { + } else if (oldState == BiometricState.STATE_AUTHENTICATING && newState == BiometricState.STATE_AUTHENTICATED) { animateIconOnce(R.drawable.face_dialog_dark_to_checkmark) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_authenticated ) - } else if (newState == STATE_PENDING_CONFIRMATION) { + } else if (newState == BiometricState.STATE_PENDING_CONFIRMATION) { animateIconOnce(R.drawable.face_dialog_wink_from_dark) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_authenticated ) - } else if (newState == STATE_IDLE) { + } else if (newState == BiometricState.STATE_IDLE) { showStaticDrawable(R.drawable.face_dialog_idle_static) iconView.contentDescription = context.getString( R.string.biometric_dialog_face_icon_description_idle diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt deleted file mode 100644 index be89d10393dd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.hardware.biometrics.BiometricAuthenticator.Modality -import android.util.AttributeSet - -/** Face only view for BiometricPrompt. */ -class AuthBiometricFaceView( - context: Context, - attrs: AttributeSet? = null -) : AuthBiometricView(context, attrs) { - - override fun getDelayAfterAuthenticatedDurationMs() = HIDE_DELAY_MS - - override fun getStateForAfterError() = STATE_IDLE - - override fun handleResetAfterError() = resetErrorView() - - override fun handleResetAfterHelp() = resetErrorView() - - override fun supportsSmallDialog() = true - - override fun supportsManualRetry() = true - - override fun supportsRequireConfirmation() = true - - override fun createIconController(): AuthIconController = - AuthBiometricFaceIconController(mContext, mIconView) - - override fun updateState(@BiometricState newState: Int) { - if (newState == STATE_AUTHENTICATING_ANIMATING_IN || - newState == STATE_AUTHENTICATING && size == AuthDialog.SIZE_MEDIUM) { - resetErrorView() - } - - // Do this last since the state variable gets updated. - super.updateState(newState) - } - - override fun onAuthenticationFailed( - @Modality modality: Int, - failureReason: String? - ) { - if (size == AuthDialog.SIZE_MEDIUM) { - if (supportsManualRetry()) { - mTryAgainButton.visibility = VISIBLE - mConfirmButton.visibility = GONE - } - } - - // Do this last since we want to know if the button is being animated (in the case of - // small -> medium dialog) - super.onAuthenticationFailed(modality, failureReason) - } - - private fun resetErrorView() { - mIndicatorView.setTextColor(mTextColorHint) - mIndicatorView.visibility = INVISIBLE - } - - companion object { - /** Delay before dismissing after being authenticated/confirmed. */ - const val HIDE_DELAY_MS = 500 - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt index 95610aec3562..fb22c6b07db4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt @@ -20,11 +20,11 @@ import android.annotation.RawRes import android.content.Context import com.airbnb.lottie.LottieAnimationView import com.android.systemui.R -import com.android.systemui.biometrics.AuthBiometricView.BiometricState -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED -import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR -import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP -import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_ERROR +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_HELP +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION /** Face/Fingerprint combined icon animator for BiometricPrompt. */ open class AuthBiometricFingerprintAndFaceIconController( @@ -36,8 +36,8 @@ open class AuthBiometricFingerprintAndFaceIconController( override val actsAsConfirmButton: Boolean = true override fun shouldAnimateIconViewForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ): Boolean = when (newState) { STATE_PENDING_CONFIRMATION -> true else -> super.shouldAnimateIconViewForTransition(oldState, newState) @@ -45,8 +45,8 @@ open class AuthBiometricFingerprintAndFaceIconController( @RawRes override fun getAnimationForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ): Int? = when (newState) { STATE_AUTHENTICATED -> { if (oldState == STATE_PENDING_CONFIRMATION) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt deleted file mode 100644 index 7ce74dbe91b4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.content.Context -import android.hardware.biometrics.BiometricAuthenticator.Modality -import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE -import android.hardware.biometrics.BiometricConstants -import android.hardware.face.FaceManager -import android.util.AttributeSet -import com.android.systemui.R - -/** Face/Fingerprint combined view for BiometricPrompt. */ -class AuthBiometricFingerprintAndFaceView( - context: Context, - attrs: AttributeSet? -) : AuthBiometricFingerprintView(context, attrs) { - var isFaceClass3 = false - - constructor (context: Context) : this(context, null) - - override fun getConfirmationPrompt() = R.string.biometric_dialog_tap_confirm_with_face - - override fun forceRequireConfirmation(@Modality modality: Int) = modality == TYPE_FACE - - override fun ignoreUnsuccessfulEventsFrom(@Modality modality: Int, unsuccessfulReason: String) = - modality == TYPE_FACE && !(isFaceClass3 && isLockoutErrorString(unsuccessfulReason)) - - override fun createIconController(): AuthIconController = - AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay) - - override fun isCoex() = true - - private fun isLockoutErrorString(unsuccessfulReason: String) = - unsuccessfulReason == FaceManager.getErrorString( - mContext, - BiometricConstants.BIOMETRIC_ERROR_LOCKOUT, - 0 /*vendorCode */ - ) || unsuccessfulReason == FaceManager.getErrorString( - mContext, - BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, - 0 /*vendorCode */ - ) -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt index d82f458cbde2..683541bd1b5b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt @@ -27,14 +27,15 @@ import androidx.annotation.VisibleForTesting import com.airbnb.lottie.LottieAnimationView import com.android.settingslib.widget.LottieColorUtils import com.android.systemui.R -import com.android.systemui.biometrics.AuthBiometricView.BiometricState -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING -import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN -import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR -import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP -import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE -import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATING +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATING_ANIMATING_IN +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_ERROR +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_HELP +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_IDLE +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION + /** Fingerprint only icon animator for BiometricPrompt. */ open class AuthBiometricFingerprintIconController( @@ -76,7 +77,7 @@ open class AuthBiometricFingerprintIconController( } } - private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) { + private fun updateIconSideFps(lastState: BiometricState, newState: BiometricState) { val displayInfo = DisplayInfo() context.display?.getDisplayInfo(displayInfo) val rotation = getRotationFromDefault(displayInfo.rotation) @@ -106,7 +107,7 @@ open class AuthBiometricFingerprintIconController( LottieColorUtils.applyDynamicColors(context, iconViewOverlay) } - private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) { + private fun updateIconNormal(lastState: BiometricState, newState: BiometricState) { val icon = getAnimationForTransition(lastState, newState) ?: return if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) { @@ -125,7 +126,7 @@ open class AuthBiometricFingerprintIconController( LottieColorUtils.applyDynamicColors(context, iconView) } - override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) { + override fun updateIcon(lastState: BiometricState, newState: BiometricState) { if (isSideFps) { updateIconSideFps(lastState, newState) } else { @@ -135,7 +136,7 @@ open class AuthBiometricFingerprintIconController( } @VisibleForTesting - fun getIconContentDescription(@BiometricState newState: Int): CharSequence? { + fun getIconContentDescription(newState: BiometricState): CharSequence? { val id = when (newState) { STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, @@ -160,8 +161,8 @@ open class AuthBiometricFingerprintIconController( } protected open fun shouldAnimateIconViewForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ) = when (newState) { STATE_HELP, STATE_ERROR -> true @@ -172,8 +173,8 @@ open class AuthBiometricFingerprintIconController( } private fun shouldAnimateSfpsIconViewForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ) = when (newState) { STATE_HELP, STATE_ERROR -> true @@ -185,8 +186,8 @@ open class AuthBiometricFingerprintIconController( } protected open fun shouldAnimateIconViewOverlayForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ) = when (newState) { STATE_HELP, STATE_ERROR -> true @@ -198,8 +199,8 @@ open class AuthBiometricFingerprintIconController( @RawRes protected open fun getAnimationForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int + oldState: BiometricState, + newState: BiometricState ): Int? { val id = when (newState) { STATE_HELP, @@ -231,8 +232,8 @@ open class AuthBiometricFingerprintIconController( @RawRes private fun getSideFpsOverlayAnimationForTransition( - @BiometricState oldState: Int, - @BiometricState newState: Int, + oldState: BiometricState, + newState: BiometricState, rotation: Int ): Int? = when (newState) { STATE_HELP, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt deleted file mode 100644 index f2e47018bf4e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.util.AttributeSet -import android.util.Log -import android.widget.FrameLayout -import android.widget.TextView -import com.android.systemui.R -import com.android.systemui.biometrics.AuthController.ScaleFactorProvider - -private const val TAG = "AuthBiometricFingerprintView" - -/** Fingerprint only view for BiometricPrompt. */ -open class AuthBiometricFingerprintView( - context: Context, - attrs: AttributeSet? = null -) : AuthBiometricView(context, attrs) { - /** If this view is for a SFPS sensor. */ - var isSfps = false - private set - - /** If this view is for a UDFPS sensor. */ - var isUdfps = false - private set - - private var udfpsAdapter: UdfpsDialogMeasureAdapter? = null - private var scaleFactorProvider: ScaleFactorProvider? = null - - /** Set the [sensorProps] of this sensor so the view can be customized prior to layout. */ - fun setSensorProperties(sensorProps: FingerprintSensorPropertiesInternal) { - isSfps = sensorProps.isAnySidefpsType - isUdfps = sensorProps.isAnyUdfpsType - udfpsAdapter = if (isUdfps) UdfpsDialogMeasureAdapter(this, sensorProps) else null - } - - fun setScaleFactorProvider(scaleProvider: ScaleFactorProvider?) { - scaleFactorProvider = scaleProvider - } - - override fun onMeasureInternal(width: Int, height: Int): AuthDialog.LayoutParams { - val layoutParams = super.onMeasureInternal(width, height) - val scale = scaleFactorProvider?.provide() ?: 1.0f - return udfpsAdapter?.onMeasureInternal(width, height, layoutParams, - scale) ?: layoutParams - } - - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - - val adapter = udfpsAdapter - if (adapter != null) { - // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen - // for devices where the UDFPS sensor is too low. - // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap - // the button bar area. - val bottomSpacerHeight = adapter.bottomSpacerHeight - Log.w(TAG, "bottomSpacerHeight: $bottomSpacerHeight") - if (bottomSpacerHeight < 0) { - val iconFrame = findViewById<FrameLayout>(R.id.biometric_icon_frame)!! - iconFrame.translationY = -bottomSpacerHeight.toFloat() - val indicator = findViewById<TextView>(R.id.indicator)!! - indicator.translationY = -bottomSpacerHeight.toFloat() - } - } - } - - override fun getDelayAfterAuthenticatedDurationMs() = 500 - - override fun getStateForAfterError() = STATE_AUTHENTICATING - - override fun handleResetAfterError() = showTouchSensorString() - - override fun handleResetAfterHelp() = showTouchSensorString() - - override fun supportsSmallDialog() = false - - override fun createIconController(): AuthIconController = - AuthBiometricFingerprintIconController(mContext, mIconView, mIconViewOverlay) - - fun updateOverrideIconLayoutParamsSize() { - udfpsAdapter?.let { - val sensorDiameter = it.getSensorDiameter(scaleFactorProvider?.provide() ?: 1.0f) - (mIconController as? AuthBiometricFingerprintIconController)?.iconLayoutParamSize = - Pair(sensorDiameter, sensorDiameter) - } - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - showTouchSensorString() - } - - private fun showTouchSensorString() { - mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor) - mIndicatorView.setTextColor(mTextColorHint) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java deleted file mode 100644 index ed4b91c7c4e4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ /dev/null @@ -1,984 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StringRes; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Insets; -import android.hardware.biometrics.BiometricAuthenticator.Modality; -import android.hardware.biometrics.BiometricPrompt; -import android.hardware.biometrics.PromptInfo; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; -import android.text.method.ScrollingMovementMethod; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.R; - -import com.airbnb.lottie.LottieAnimationView; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; - -/** - * Contains the Biometric views (title, subtitle, icon, buttons, etc.) and its controllers. - */ -public abstract class AuthBiometricView extends LinearLayout implements AuthBiometricViewAdapter { - - private static final String TAG = "AuthBiometricView"; - - /** - * Authentication hardware idle. - */ - public static final int STATE_IDLE = 0; - /** - * UI animating in, authentication hardware active. - */ - public static final int STATE_AUTHENTICATING_ANIMATING_IN = 1; - /** - * UI animated in, authentication hardware active. - */ - public static final int STATE_AUTHENTICATING = 2; - /** - * UI animated in, authentication hardware active. - */ - public static final int STATE_HELP = 3; - /** - * Hard error, e.g. ERROR_TIMEOUT. Authentication hardware idle. - */ - public static final int STATE_ERROR = 4; - /** - * Authenticated, waiting for user confirmation. Authentication hardware idle. - */ - public static final int STATE_PENDING_CONFIRMATION = 5; - /** - * Authenticated, dialog animating away soon. - */ - public static final int STATE_AUTHENTICATED = 6; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP, - STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED}) - @interface BiometricState {} - - /** - * Callback to the parent when a user action has occurred. - */ - public interface Callback { - int ACTION_AUTHENTICATED = 1; - int ACTION_USER_CANCELED = 2; - int ACTION_BUTTON_NEGATIVE = 3; - int ACTION_BUTTON_TRY_AGAIN = 4; - int ACTION_ERROR = 5; - int ACTION_USE_DEVICE_CREDENTIAL = 6; - int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7; - int ACTION_AUTHENTICATED_AND_CONFIRMED = 8; - - /** - * When an action has occurred. The caller will only invoke this when the callback should - * be propagated. e.g. the caller will handle any necessary delay. - * @param action - */ - void onAction(int action); - } - - private final Handler mHandler; - private final AccessibilityManager mAccessibilityManager; - private final LockPatternUtils mLockPatternUtils; - protected final int mTextColorError; - protected final int mTextColorHint; - - private AuthPanelController mPanelController; - - private PromptInfo mPromptInfo; - private boolean mRequireConfirmation; - private int mUserId; - private int mEffectiveUserId; - private @AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN; - - private TextView mTitleView; - private TextView mSubtitleView; - private TextView mDescriptionView; - private View mIconHolderView; - protected LottieAnimationView mIconViewOverlay; - protected LottieAnimationView mIconView; - protected TextView mIndicatorView; - - @VisibleForTesting @NonNull AuthIconController mIconController; - @VisibleForTesting int mAnimationDurationShort = AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS; - @VisibleForTesting int mAnimationDurationLong = AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS; - @VisibleForTesting int mAnimationDurationHideDialog = BiometricPrompt.HIDE_DIALOG_DELAY; - - // Negative button position, exclusively for the app-specified behavior - @VisibleForTesting Button mNegativeButton; - // Negative button position, exclusively for cancelling auth after passive auth success - @VisibleForTesting Button mCancelButton; - // Negative button position, shown if device credentials are allowed - @VisibleForTesting Button mUseCredentialButton; - - // Positive button position, - @VisibleForTesting Button mConfirmButton; - @VisibleForTesting Button mTryAgainButton; - - // Measurements when biometric view is showing text, buttons, etc. - @Nullable @VisibleForTesting AuthDialog.LayoutParams mLayoutParams; - - private Callback mCallback; - @BiometricState private int mState; - - private float mIconOriginalY; - - protected boolean mDialogSizeAnimating; - protected Bundle mSavedState; - - private final Runnable mResetErrorRunnable; - private final Runnable mResetHelpRunnable; - - private Animator.AnimatorListener mJankListener; - - private final boolean mUseCustomBpSize; - private final int mCustomBpWidth; - private final int mCustomBpHeight; - - private final OnClickListener mBackgroundClickListener = (view) -> { - if (mState == STATE_AUTHENTICATED) { - Log.w(TAG, "Ignoring background click after authenticated"); - return; - } else if (mSize == AuthDialog.SIZE_SMALL) { - Log.w(TAG, "Ignoring background click during small dialog"); - return; - } else if (mSize == AuthDialog.SIZE_LARGE) { - Log.w(TAG, "Ignoring background click during large dialog"); - return; - } - mCallback.onAction(Callback.ACTION_USER_CANCELED); - }; - - public AuthBiometricView(Context context) { - this(context, null); - } - - public AuthBiometricView(Context context, AttributeSet attrs) { - super(context, attrs); - mHandler = new Handler(Looper.getMainLooper()); - mTextColorError = getResources().getColor( - R.color.biometric_dialog_error, context.getTheme()); - mTextColorHint = getResources().getColor( - R.color.biometric_dialog_gray, context.getTheme()); - - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - mLockPatternUtils = new LockPatternUtils(context); - - mResetErrorRunnable = () -> { - updateState(getStateForAfterError()); - handleResetAfterError(); - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - }; - - mResetHelpRunnable = () -> { - updateState(STATE_AUTHENTICATING); - handleResetAfterHelp(); - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - }; - - mUseCustomBpSize = getResources().getBoolean(R.bool.use_custom_bp_size); - mCustomBpWidth = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_width); - mCustomBpHeight = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_height); - } - - /** Delay after authentication is confirmed, before the dialog should be animated away. */ - protected int getDelayAfterAuthenticatedDurationMs() { - return 0; - } - - /** State that the dialog/icon should be in after showing a help message. */ - protected int getStateForAfterError() { - return STATE_IDLE; - } - - /** Invoked when the error message is being cleared. */ - protected void handleResetAfterError() {} - - /** Invoked when the help message is being cleared. */ - protected void handleResetAfterHelp() {} - - /** True if the dialog supports {@link AuthDialog.DialogSize#SIZE_SMALL}. */ - protected boolean supportsSmallDialog() { - return false; - } - - /** The string to show when the user must tap to confirm via the button or icon. */ - @StringRes - protected int getConfirmationPrompt() { - return R.string.biometric_dialog_tap_confirm; - } - - /** True if require confirmation will be honored when set via the API. */ - protected boolean supportsRequireConfirmation() { - return false; - } - - /** True if confirmation will be required even if it was not supported/requested. */ - protected boolean forceRequireConfirmation(@Modality int modality) { - return false; - } - - /** Ignore all events from this (secondary) modality except successful authentication. */ - protected boolean ignoreUnsuccessfulEventsFrom(@Modality int modality, - String unsuccessfulReason) { - return false; - } - - /** Create the controller for managing the icons transitions during the prompt.*/ - @NonNull - protected abstract AuthIconController createIconController(); - - @Override - public AuthIconController getLegacyIconController() { - return mIconController; - } - - @Override - public void cancelAnimation() { - animate().cancel(); - } - - @Override - public View asView() { - return this; - } - - @Override - public boolean isCoex() { - return false; - } - - void setPanelController(AuthPanelController panelController) { - mPanelController = panelController; - } - void setPromptInfo(PromptInfo promptInfo) { - mPromptInfo = promptInfo; - } - - void setCallback(Callback callback) { - mCallback = callback; - } - - void setBackgroundView(View backgroundView) { - backgroundView.setOnClickListener(mBackgroundClickListener); - } - - void setUserId(int userId) { - mUserId = userId; - } - - void setEffectiveUserId(int effectiveUserId) { - mEffectiveUserId = effectiveUserId; - } - - void setRequireConfirmation(boolean requireConfirmation) { - mRequireConfirmation = requireConfirmation && supportsRequireConfirmation(); - } - - void setJankListener(Animator.AnimatorListener jankListener) { - mJankListener = jankListener; - } - - private void updatePaddings(int size) { - final Insets navBarInsets = Utils.getNavbarInsets(mContext); - if (size != AuthDialog.SIZE_LARGE) { - if (mPanelController.getPosition() == AuthPanelController.POSITION_LEFT) { - setPadding(navBarInsets.left, 0, 0, 0); - } else if (mPanelController.getPosition() == AuthPanelController.POSITION_RIGHT) { - setPadding(0, 0, navBarInsets.right, 0); - } else { - setPadding(0, 0, 0, navBarInsets.bottom); - } - } else { - setPadding(0, 0, 0, 0); - } - } - - @VisibleForTesting - final void updateSize(@AuthDialog.DialogSize int newSize) { - Log.v(TAG, "Current size: " + mSize + " New size: " + newSize); - updatePaddings(newSize); - if (newSize == AuthDialog.SIZE_SMALL) { - mTitleView.setVisibility(View.GONE); - mSubtitleView.setVisibility(View.GONE); - mDescriptionView.setVisibility(View.GONE); - mIndicatorView.setVisibility(View.GONE); - mNegativeButton.setVisibility(View.GONE); - mUseCredentialButton.setVisibility(View.GONE); - - final float iconPadding = getResources() - .getDimension(R.dimen.biometric_dialog_icon_padding); - mIconHolderView.setY(getHeight() - mIconHolderView.getHeight() - iconPadding); - - // Subtract the vertical padding from the new height since it's only used to create - // extra space between the other elements, and not part of the actual icon. - final int newHeight = mIconHolderView.getHeight() + 2 * (int) iconPadding - - mIconHolderView.getPaddingTop() - mIconHolderView.getPaddingBottom(); - mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, newHeight, - 0 /* animateDurationMs */); - - mSize = newSize; - } else if (mSize == AuthDialog.SIZE_SMALL && newSize == AuthDialog.SIZE_MEDIUM) { - if (mDialogSizeAnimating) { - return; - } - mDialogSizeAnimating = true; - - // Animate the icon back to original position - final ValueAnimator iconAnimator = - ValueAnimator.ofFloat(mIconHolderView.getY(), mIconOriginalY); - iconAnimator.addUpdateListener((animation) -> { - mIconHolderView.setY((float) animation.getAnimatedValue()); - }); - - // Animate the text - final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1); - opacityAnimator.addUpdateListener((animation) -> { - final float opacity = (float) animation.getAnimatedValue(); - mTitleView.setAlpha(opacity); - mIndicatorView.setAlpha(opacity); - mNegativeButton.setAlpha(opacity); - mCancelButton.setAlpha(opacity); - mTryAgainButton.setAlpha(opacity); - - if (!TextUtils.isEmpty(mSubtitleView.getText())) { - mSubtitleView.setAlpha(opacity); - } - if (!TextUtils.isEmpty(mDescriptionView.getText())) { - mDescriptionView.setAlpha(opacity); - } - }); - - // Choreograph together - final AnimatorSet as = new AnimatorSet(); - as.setDuration(mAnimationDurationShort); - as.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - mTitleView.setVisibility(View.VISIBLE); - mIndicatorView.setVisibility(View.VISIBLE); - - if (isDeviceCredentialAllowed()) { - mUseCredentialButton.setVisibility(View.VISIBLE); - } else { - mNegativeButton.setVisibility(View.VISIBLE); - } - if (supportsManualRetry()) { - mTryAgainButton.setVisibility(View.VISIBLE); - } - - if (!TextUtils.isEmpty(mSubtitleView.getText())) { - mSubtitleView.setVisibility(View.VISIBLE); - } - if (!TextUtils.isEmpty(mDescriptionView.getText())) { - mDescriptionView.setVisibility(View.VISIBLE); - } - } - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mSize = newSize; - mDialogSizeAnimating = false; - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, - AuthBiometricView.this); - } - }); - - if (mJankListener != null) { - as.addListener(mJankListener); - } - as.play(iconAnimator).with(opacityAnimator); - as.start(); - // Animate the panel - mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, - mLayoutParams.mMediumHeight, - AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS); - } else if (newSize == AuthDialog.SIZE_MEDIUM) { - mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, - mLayoutParams.mMediumHeight, - 0 /* animateDurationMs */); - mSize = newSize; - } else if (newSize == AuthDialog.SIZE_LARGE) { - final float translationY = getResources().getDimension( - R.dimen.biometric_dialog_medium_to_large_translation_offset); - final AuthBiometricView biometricView = this; - - // Translate at full duration - final ValueAnimator translationAnimator = ValueAnimator.ofFloat( - biometricView.getY(), biometricView.getY() - translationY); - translationAnimator.setDuration(mAnimationDurationLong); - translationAnimator.addUpdateListener((animation) -> { - final float translation = (float) animation.getAnimatedValue(); - biometricView.setTranslationY(translation); - }); - translationAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - if (biometricView.getParent() instanceof ViewGroup) { - ((ViewGroup) biometricView.getParent()).removeView(biometricView); - } - mSize = newSize; - } - }); - - // Opacity to 0 in half duration - final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(1, 0); - opacityAnimator.setDuration(mAnimationDurationLong / 2); - opacityAnimator.addUpdateListener((animation) -> { - final float opacity = (float) animation.getAnimatedValue(); - biometricView.setAlpha(opacity); - }); - - mPanelController.setUseFullScreen(true); - mPanelController.updateForContentDimensions( - mPanelController.getContainerWidth(), - mPanelController.getContainerHeight(), - mAnimationDurationLong); - - // Start the animations together - AnimatorSet as = new AnimatorSet(); - List<Animator> animators = new ArrayList<>(); - animators.add(translationAnimator); - animators.add(opacityAnimator); - - if (mJankListener != null) { - as.addListener(mJankListener); - } - as.playTogether(animators); - as.setDuration(mAnimationDurationLong * 2 / 3); - as.start(); - } else { - Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize); - } - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - } - - protected boolean supportsManualRetry() { - return false; - } - - /** - * Updates mIconView animation on updates to fold state, device rotation, or rear display mode - * @param animation new asset to use for iconw - */ - public void updateIconViewAnimation(int animation) { - mIconView.setAnimation(animation); - } - - public void updateState(@BiometricState int newState) { - Log.d(TAG, "newState: " + newState); - - mIconController.updateState(mState, newState); - - switch (newState) { - case STATE_AUTHENTICATING_ANIMATING_IN: - case STATE_AUTHENTICATING: - removePendingAnimations(); - if (mRequireConfirmation) { - mConfirmButton.setEnabled(false); - mConfirmButton.setVisibility(View.VISIBLE); - } - break; - - case STATE_AUTHENTICATED: - removePendingAnimations(); - if (mSize != AuthDialog.SIZE_SMALL) { - mConfirmButton.setVisibility(View.GONE); - mNegativeButton.setVisibility(View.GONE); - mUseCredentialButton.setVisibility(View.GONE); - mCancelButton.setVisibility(View.GONE); - mIndicatorView.setVisibility(View.INVISIBLE); - } - announceForAccessibility(getResources() - .getString(R.string.biometric_dialog_authenticated)); - if (mState == STATE_PENDING_CONFIRMATION) { - mHandler.postDelayed(() -> mCallback.onAction( - Callback.ACTION_AUTHENTICATED_AND_CONFIRMED), - getDelayAfterAuthenticatedDurationMs()); - } else { - mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), - getDelayAfterAuthenticatedDurationMs()); - } - break; - - case STATE_PENDING_CONFIRMATION: - removePendingAnimations(); - mNegativeButton.setVisibility(View.GONE); - mCancelButton.setVisibility(View.VISIBLE); - mUseCredentialButton.setVisibility(View.GONE); - // forced confirmations (multi-sensor) use the icon view as the confirm button - mConfirmButton.setEnabled(mRequireConfirmation); - mConfirmButton.setVisibility(mRequireConfirmation ? View.VISIBLE : View.GONE); - mIndicatorView.setTextColor(mTextColorHint); - mIndicatorView.setText(getConfirmationPrompt()); - mIndicatorView.setVisibility(View.VISIBLE); - break; - - case STATE_ERROR: - if (mSize == AuthDialog.SIZE_SMALL) { - updateSize(AuthDialog.SIZE_MEDIUM); - } - break; - - default: - Log.w(TAG, "Unhandled state: " + newState); - break; - } - - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - mState = newState; - } - - public void onOrientationChanged() { - // Update padding and AuthPanel outline by calling updateSize when the orientation changed. - updateSize(mSize); - } - - public void onDialogAnimatedIn(boolean fingerprintWasStarted) { - updateState(STATE_AUTHENTICATING); - } - - public void onAuthenticationSucceeded(@Modality int modality) { - removePendingAnimations(); - if (mRequireConfirmation || forceRequireConfirmation(modality)) { - updateState(STATE_PENDING_CONFIRMATION); - } else { - updateState(STATE_AUTHENTICATED); - } - } - - /** - * Notify the view that auth has failed. - * - * @param modality sensor modality that failed - * @param failureReason message - */ - public void onAuthenticationFailed( - @Modality int modality, @Nullable String failureReason) { - if (ignoreUnsuccessfulEventsFrom(modality, failureReason)) { - return; - } - - showTemporaryMessage(failureReason, mResetErrorRunnable); - updateState(STATE_ERROR); - } - - /** - * Notify the view that an error occurred. - * - * @param modality sensor modality that failed - * @param error message - */ - public void onError(@Modality int modality, String error) { - if (ignoreUnsuccessfulEventsFrom(modality, error)) { - return; - } - - showTemporaryMessage(error, mResetErrorRunnable); - updateState(STATE_ERROR); - - mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_ERROR), - mAnimationDurationHideDialog); - } - - /** - * Show a help message to the user. - * - * @param modality sensor modality - * @param help message - */ - public void onHelp(@Modality int modality, String help) { - if (ignoreUnsuccessfulEventsFrom(modality, help)) { - return; - } - if (mSize != AuthDialog.SIZE_MEDIUM) { - Log.w(TAG, "Help received in size: " + mSize); - return; - } - if (TextUtils.isEmpty(help)) { - Log.w(TAG, "Ignoring blank help message"); - return; - } - - showTemporaryMessage(help, mResetHelpRunnable); - updateState(STATE_HELP); - } - - public void onSaveState(@NonNull Bundle outState) { - outState.putInt(AuthDialog.KEY_BIOMETRIC_CONFIRM_VISIBILITY, - mConfirmButton.getVisibility()); - outState.putInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY, - mTryAgainButton.getVisibility()); - outState.putInt(AuthDialog.KEY_BIOMETRIC_STATE, mState); - outState.putString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING, - mIndicatorView.getText() != null ? mIndicatorView.getText().toString() : ""); - outState.putBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING, - mHandler.hasCallbacks(mResetErrorRunnable)); - outState.putBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING, - mHandler.hasCallbacks(mResetHelpRunnable)); - outState.putInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE, mSize); - } - - /** - * Invoked after inflation but before being attached to window. - * @param savedState - */ - public void restoreState(@Nullable Bundle savedState) { - mSavedState = savedState; - } - private void setTextOrHide(TextView view, CharSequence charSequence) { - if (TextUtils.isEmpty(charSequence)) { - view.setVisibility(View.GONE); - } else { - view.setText(charSequence); - } - - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - } - - // Remove all pending icon and text animations - private void removePendingAnimations() { - mHandler.removeCallbacks(mResetHelpRunnable); - mHandler.removeCallbacks(mResetErrorRunnable); - } - - private void showTemporaryMessage(String message, Runnable resetMessageRunnable) { - removePendingAnimations(); - mIndicatorView.setText(message); - mIndicatorView.setTextColor(mTextColorError); - mIndicatorView.setVisibility(View.VISIBLE); - // select to enable marquee unless a screen reader is enabled - mIndicatorView.setSelected(!mAccessibilityManager.isEnabled() - || !mAccessibilityManager.isTouchExplorationEnabled()); - mHandler.postDelayed(resetMessageRunnable, mAnimationDurationHideDialog); - - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (mSavedState != null) { - updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE)); - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mTitleView = findViewById(R.id.title); - mSubtitleView = findViewById(R.id.subtitle); - mDescriptionView = findViewById(R.id.description); - mIconViewOverlay = findViewById(R.id.biometric_icon_overlay); - mIconView = findViewById(R.id.biometric_icon); - mIconHolderView = findViewById(R.id.biometric_icon_frame); - mIndicatorView = findViewById(R.id.indicator); - - // Negative-side (left) buttons - mNegativeButton = findViewById(R.id.button_negative); - mCancelButton = findViewById(R.id.button_cancel); - mUseCredentialButton = findViewById(R.id.button_use_credential); - - // Positive-side (right) buttons - mConfirmButton = findViewById(R.id.button_confirm); - mTryAgainButton = findViewById(R.id.button_try_again); - - mNegativeButton.setOnClickListener((view) -> { - mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE); - }); - - mCancelButton.setOnClickListener((view) -> { - mCallback.onAction(Callback.ACTION_USER_CANCELED); - }); - - mUseCredentialButton.setOnClickListener((view) -> { - startTransitionToCredentialUI(false /* isError */); - }); - - mConfirmButton.setOnClickListener((view) -> { - updateState(STATE_AUTHENTICATED); - }); - - mTryAgainButton.setOnClickListener((view) -> { - updateState(STATE_AUTHENTICATING); - mCallback.onAction(Callback.ACTION_BUTTON_TRY_AGAIN); - mTryAgainButton.setVisibility(View.GONE); - Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); - }); - - mIconController = createIconController(); - if (mIconController.getActsAsConfirmButton()) { - mIconViewOverlay.setOnClickListener((view)->{ - if (mState == STATE_PENDING_CONFIRMATION) { - updateState(STATE_AUTHENTICATED); - } - }); - mIconView.setOnClickListener((view) -> { - if (mState == STATE_PENDING_CONFIRMATION) { - updateState(STATE_AUTHENTICATED); - } - }); - } - } - - /** - * Kicks off the animation process and invokes the callback. - * - * @param isError if this was triggered due to an error and not a user action (unused, - * previously for haptics). - */ - @Override - public void startTransitionToCredentialUI(boolean isError) { - updateSize(AuthDialog.SIZE_LARGE); - mCallback.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - mTitleView.setText(mPromptInfo.getTitle()); - - // setSelected could make marquee work - mTitleView.setSelected(true); - mSubtitleView.setSelected(true); - // make description view become scrollable - mDescriptionView.setMovementMethod(new ScrollingMovementMethod()); - - if (isDeviceCredentialAllowed()) { - final CharSequence credentialButtonText; - @Utils.CredentialType final int credentialType = - Utils.getCredentialType(mLockPatternUtils, mEffectiveUserId); - switch (credentialType) { - case Utils.CREDENTIAL_PIN: - credentialButtonText = - getResources().getString(R.string.biometric_dialog_use_pin); - break; - case Utils.CREDENTIAL_PATTERN: - credentialButtonText = - getResources().getString(R.string.biometric_dialog_use_pattern); - break; - case Utils.CREDENTIAL_PASSWORD: - default: - credentialButtonText = - getResources().getString(R.string.biometric_dialog_use_password); - break; - } - - mNegativeButton.setVisibility(View.GONE); - - mUseCredentialButton.setText(credentialButtonText); - mUseCredentialButton.setVisibility(View.VISIBLE); - } else { - mNegativeButton.setText(mPromptInfo.getNegativeButtonText()); - } - - setTextOrHide(mSubtitleView, mPromptInfo.getSubtitle()); - setTextOrHide(mDescriptionView, mPromptInfo.getDescription()); - - if (mSavedState == null) { - updateState(STATE_AUTHENTICATING_ANIMATING_IN); - } else { - // Restore as much state as possible first - updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE)); - - // Restore positive button(s) state - mConfirmButton.setVisibility( - mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_CONFIRM_VISIBILITY)); - if (mConfirmButton.getVisibility() == View.GONE) { - setRequireConfirmation(false); - } - mTryAgainButton.setVisibility( - mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY)); - - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - mIconController.setDeactivated(true); - - // Empty the handler, otherwise things like ACTION_AUTHENTICATED may be duplicated once - // the new dialog is restored. - mHandler.removeCallbacksAndMessages(null /* all */); - } - - /** - * Contains all of the testable logic that should be invoked when {@link #onMeasure(int, int)} - * is invoked. In addition, this allows subclasses to implement custom measuring logic while - * allowing the base class to have common code to apply the custom measurements. - * - * @param width Width to constrain the measurements to. - * @param height Height to constrain the measurements to. - * @return See {@link AuthDialog.LayoutParams} - */ - @NonNull - AuthDialog.LayoutParams onMeasureInternal(int width, int height) { - int totalHeight = 0; - final int numChildren = getChildCount(); - for (int i = 0; i < numChildren; i++) { - final View child = getChildAt(i); - - if (child.getId() == R.id.space_above_icon - || child.getId() == R.id.space_below_icon - || child.getId() == R.id.button_bar) { - child.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, - MeasureSpec.EXACTLY)); - } else if (child.getId() == R.id.biometric_icon_frame) { - final View iconView = findViewById(R.id.biometric_icon); - child.measure( - MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().width, - MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height, - MeasureSpec.EXACTLY)); - } else if (child.getId() == R.id.biometric_icon) { - child.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); - } else { - child.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); - } - - if (child.getVisibility() != View.GONE) { - totalHeight += child.getMeasuredHeight(); - } - } - - return new AuthDialog.LayoutParams(width, totalHeight); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = MeasureSpec.getSize(widthMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); - - if (mUseCustomBpSize) { - width = mCustomBpWidth; - height = mCustomBpHeight; - } else { - width = Math.min(width, height); - } - - mLayoutParams = onMeasureInternal(width, height); - - final Insets navBarInsets = Utils.getNavbarInsets(mContext); - final int navBarHeight = navBarInsets.bottom; - final int navBarWidth; - if (mPanelController.getPosition() == AuthPanelController.POSITION_LEFT) { - navBarWidth = navBarInsets.left; - } else if (mPanelController.getPosition() == AuthPanelController.POSITION_RIGHT) { - navBarWidth = navBarInsets.right; - } else { - navBarWidth = 0; - } - - // The actual auth dialog w/h should include navigation bar size. - if (navBarWidth != 0 || navBarHeight != 0) { - mLayoutParams = new AuthDialog.LayoutParams( - mLayoutParams.mMediumWidth + navBarWidth, - mLayoutParams.mMediumHeight + navBarInsets.bottom); - } - - setMeasuredDimension(mLayoutParams.mMediumWidth, mLayoutParams.mMediumHeight); - } - - @Override - public void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - // Start with initial size only once. Subsequent layout changes don't matter since we - // only care about the initial icon position. - if (mIconOriginalY == 0) { - mIconOriginalY = mIconHolderView.getY(); - if (mSavedState == null) { - updateSize(!mRequireConfirmation && supportsSmallDialog() ? AuthDialog.SIZE_SMALL - : AuthDialog.SIZE_MEDIUM); - } else { - updateSize(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE)); - - // Restore indicator text state only after size has been restored - final String indicatorText = - mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING); - if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) { - onHelp(TYPE_NONE, indicatorText); - } else if (mSavedState.getBoolean( - AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) { - onAuthenticationFailed(TYPE_NONE, indicatorText); - } - } - } - } - - private boolean isDeviceCredentialAllowed() { - return Utils.isDeviceCredentialAllowed(mPromptInfo); - } - - public LottieAnimationView getIconView() { - return mIconView; - } - - @AuthDialog.DialogSize int getSize() { - return mSize; - } - - /** If authentication has successfully occurred and the view is done. */ - boolean isAuthenticated() { - return mState == STATE_AUTHENTICATED; - } - - /** If authentication is currently in progress. */ - boolean isAuthenticating() { - return mState == STATE_AUTHENTICATING; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricViewAdapter.kt deleted file mode 100644 index 68db564606fd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricViewAdapter.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.hardware.biometrics.BiometricAuthenticator -import android.os.Bundle -import android.view.View - -/** TODO(b/251476085): Temporary interface while legacy biometric prompt is around. */ -@Deprecated("temporary adapter while migrating biometric prompt - do not expand") -interface AuthBiometricViewAdapter { - val legacyIconController: AuthIconController? - - fun onDialogAnimatedIn(fingerprintWasStarted: Boolean) - - fun onAuthenticationSucceeded(@BiometricAuthenticator.Modality modality: Int) - - fun onAuthenticationFailed( - @BiometricAuthenticator.Modality modality: Int, - failureReason: String - ) - - fun onError(@BiometricAuthenticator.Modality modality: Int, error: String) - - fun onHelp(@BiometricAuthenticator.Modality modality: Int, help: String) - - fun startTransitionToCredentialUI(isError: Boolean) - - fun requestLayout() - - fun onSaveState(bundle: Bundle?) - - fun restoreState(bundle: Bundle?) - - fun onOrientationChanged() - - fun cancelAnimation() - - fun isCoex(): Boolean - - fun asView(): View -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 20b5f78bd70c..c7d7fe32be31 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -17,7 +17,6 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; @@ -34,7 +33,6 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -73,11 +71,12 @@ import com.android.systemui.biometrics.shared.model.BiometricModalities; import com.android.systemui.biometrics.ui.BiometricPromptLayout; import com.android.systemui.biometrics.ui.CredentialView; import com.android.systemui.biometrics.ui.binder.BiometricViewBinder; +import com.android.systemui.biometrics.ui.binder.BiometricViewSizeBinder; +import com.android.systemui.biometrics.ui.binder.Spaghetti; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -95,7 +94,10 @@ import kotlinx.coroutines.CoroutineScope; /** * Top level container/controller for the BiometricPrompt UI. + * + * @deprecated TODO(b/287311775): remove and merge view/layouts into new prompt. */ +@Deprecated public class AuthContainerView extends LinearLayout implements AuthDialog, WakefulnessLifecycle.Observer, CredentialView.Host { @@ -103,6 +105,7 @@ public class AuthContainerView extends LinearLayout private static final int ANIMATION_DURATION_SHOW_MS = 250; private static final int ANIMATION_DURATION_AWAY_MS = 350; + private static final int ANIMATE_CREDENTIAL_START_DELAY_MS = 300; private static final int STATE_UNKNOWN = 0; private static final int STATE_ANIMATING_IN = 1; @@ -137,16 +140,16 @@ public class AuthContainerView extends LinearLayout private final InteractionJankMonitor mInteractionJankMonitor; private final CoroutineScope mApplicationCoroutineScope; - // TODO: these should be migrated out once ready + // TODO(b/287311775): these should be migrated out once ready private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor; private final @NonNull Provider<PromptSelectorInteractor> mPromptSelectorInteractorProvider; - // TODO(b/251476085): these should be migrated out of the view + // TODO(b/287311775): these should be migrated out of the view private final Provider<CredentialViewModel> mCredentialViewModelProvider; private final PromptViewModel mPromptViewModel; @VisibleForTesting final BiometricCallback mBiometricCallback; - @Nullable private AuthBiometricViewAdapter mBiometricView; + @Nullable private Spaghetti mBiometricView; @Nullable private View mCredentialView; private final AuthPanelController mPanelController; private final FrameLayout mFrameLayout; @@ -165,7 +168,7 @@ public class AuthContainerView extends LinearLayout // HAT received from LockSettingsService when credential is verified. @Nullable private byte[] mCredentialAttestation; - // TODO(b/251476085): remove when legacy prompt is replaced + // TODO(b/287311775): remove when legacy prompt is replaced @Deprecated static class Config { Context mContext; @@ -183,42 +186,50 @@ public class AuthContainerView extends LinearLayout } @VisibleForTesting - final class BiometricCallback implements AuthBiometricView.Callback { + final class BiometricCallback implements Spaghetti.Callback { @Override - public void onAction(int action) { - switch (action) { - case AuthBiometricView.Callback.ACTION_AUTHENTICATED: - animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); - break; - case AuthBiometricView.Callback.ACTION_USER_CANCELED: - sendEarlyUserCanceled(); - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); - break; - case AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE: - animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); - break; - case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN: - mFailedModalities.clear(); - mConfig.mCallback.onTryAgainPressed(getRequestId()); - break; - case AuthBiometricView.Callback.ACTION_ERROR: - animateAway(AuthDialogCallback.DISMISSED_ERROR); - break; - case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL: - mConfig.mCallback.onDeviceCredentialPressed(getRequestId()); - mHandler.postDelayed(() -> { - addCredentialView(false /* animatePanel */, true /* animateContents */); - }, mConfig.mSkipAnimation ? 0 : AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS); - break; - case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR: - mConfig.mCallback.onStartFingerprintNow(getRequestId()); - break; - case AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED: - animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); - break; - default: - Log.e(TAG, "Unhandled action: " + action); - } + public void onAuthenticated() { + animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); + } + + @Override + public void onUserCanceled() { + sendEarlyUserCanceled(); + animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + } + + @Override + public void onButtonNegative() { + animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); + } + + @Override + public void onButtonTryAgain() { + mFailedModalities.clear(); + mConfig.mCallback.onTryAgainPressed(getRequestId()); + } + + @Override + public void onError() { + animateAway(AuthDialogCallback.DISMISSED_ERROR); + } + + @Override + public void onUseDeviceCredential() { + mConfig.mCallback.onDeviceCredentialPressed(getRequestId()); + mHandler.postDelayed(() -> { + addCredentialView(false /* animatePanel */, true /* animateContents */); + }, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS); + } + + @Override + public void onStartDelayedFingerprintSensor() { + mConfig.mCallback.onStartFingerprintNow(getRequestId()); + } + + @Override + public void onAuthenticatedAndConfirmed() { + animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); } } @@ -354,14 +365,10 @@ public class AuthContainerView extends LinearLayout mCredentialViewModelProvider = credentialViewModelProvider; mPromptViewModel = promptViewModel; - if (featureFlags.isEnabled(Flags.BIOMETRIC_BP_STRONG)) { - showPrompt(config, layoutInflater, promptViewModel, - Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds), - Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds), - vibratorHelper, featureFlags); - } else { - showLegacyPrompt(config, layoutInflater, fpProps, faceProps); - } + showPrompt(config, layoutInflater, promptViewModel, + Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds), + Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds), + vibratorHelper, featureFlags); // TODO: De-dupe the logic with AuthCredentialPasswordView setOnKeyListener((v, keyCode, event) -> { @@ -397,7 +404,8 @@ public class AuthContainerView extends LinearLayout R.layout.biometric_prompt_layout, null, false); mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController, // TODO(b/201510778): This uses the wrong timeout in some cases - getJankListener(view, TRANSIT, AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), + getJankListener(view, TRANSIT, + BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, vibratorHelper, featureFlags); @@ -411,60 +419,6 @@ public class AuthContainerView extends LinearLayout } } - // TODO(b/251476085): remove entirely - private void showLegacyPrompt(@NonNull Config config, @NonNull LayoutInflater layoutInflater, - @Nullable List<FingerprintSensorPropertiesInternal> fpProps, - @Nullable List<FaceSensorPropertiesInternal> faceProps - ) { - // Inflate biometric view only if necessary. - if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { - final FingerprintSensorPropertiesInternal fpProperties = - Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds); - final FaceSensorPropertiesInternal faceProperties = - Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds); - - if (fpProperties != null && faceProperties != null) { - final AuthBiometricFingerprintAndFaceView fingerprintAndFaceView = - (AuthBiometricFingerprintAndFaceView) layoutInflater.inflate( - R.layout.auth_biometric_fingerprint_and_face_view, null, false); - fingerprintAndFaceView.setSensorProperties(fpProperties); - fingerprintAndFaceView.setScaleFactorProvider(config.mScaleProvider); - fingerprintAndFaceView.updateOverrideIconLayoutParamsSize(); - fingerprintAndFaceView.setFaceClass3( - faceProperties.sensorStrength == STRENGTH_STRONG); - mBiometricView = fingerprintAndFaceView; - } else if (fpProperties != null) { - final AuthBiometricFingerprintView fpView = - (AuthBiometricFingerprintView) layoutInflater.inflate( - R.layout.auth_biometric_fingerprint_view, null, false); - fpView.setSensorProperties(fpProperties); - fpView.setScaleFactorProvider(config.mScaleProvider); - fpView.updateOverrideIconLayoutParamsSize(); - mBiometricView = fpView; - } else if (faceProperties != null) { - mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate( - R.layout.auth_biometric_face_view, null, false); - } else { - Log.e(TAG, "No sensors found!"); - } - } - - // init view before showing - if (mBiometricView != null) { - final AuthBiometricView view = (AuthBiometricView) mBiometricView; - view.setRequireConfirmation(mConfig.mRequireConfirmation); - view.setPanelController(mPanelController); - view.setPromptInfo(mConfig.mPromptInfo); - view.setCallback(mBiometricCallback); - view.setBackgroundView(mBackgroundView); - view.setUserId(mConfig.mUserId); - view.setEffectiveUserId(mEffectiveUserId); - // TODO(b/201510778): This uses the wrong timeout in some cases (remove w/ above) - view.setJankListener(getJankListener(view, TRANSIT, - AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS)); - } - } - private void onBackInvoked() { sendEarlyUserCanceled(); animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); @@ -532,9 +486,6 @@ public class AuthContainerView extends LinearLayout @Override public void onOrientationChanged() { maybeUpdatePositionForUdfps(true /* invalidate */); - if (mBiometricView != null) { - mBiometricView.onOrientationChanged(); - } } @Override @@ -620,10 +571,6 @@ public class AuthContainerView extends LinearLayout } private static boolean shouldUpdatePositionForUdfps(@NonNull View view) { - // TODO(b/251476085): legacy view (delete when removed) - if (view instanceof AuthBiometricFingerprintView) { - return ((AuthBiometricFingerprintView) view).isUdfps(); - } if (view instanceof BiometricPromptLayout) { // this will force the prompt to align itself on the edge of the screen // instead of centering (temporary workaround to prevent small implicit view @@ -671,7 +618,6 @@ public class AuthContainerView extends LinearLayout if (invalidate) { mPanelView.invalidateOutline(); - mBiometricView.requestLayout(); } return true; @@ -701,11 +647,7 @@ public class AuthContainerView extends LinearLayout } @Override - public void show(WindowManager wm, @Nullable Bundle savedState) { - if (mBiometricView != null) { - mBiometricView.restoreState(savedState); - } - + public void show(WindowManager wm) { wm.addView(this, getLayoutParams(mWindowToken, mConfig.mPromptInfo.getTitle())); } @@ -780,7 +722,7 @@ public class AuthContainerView extends LinearLayout if (mFailedModalities.contains(TYPE_FACE)) { Log.d(TAG, "retrying failed modalities (pointer down)"); mFailedModalities.remove(TYPE_FACE); - mBiometricCallback.onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN); + mBiometricCallback.onButtonTryAgain(); } } else { Log.e(TAG, "onPointerDown(): mBiometricView is null"); @@ -788,21 +730,6 @@ public class AuthContainerView extends LinearLayout } @Override - public void onSaveState(@NonNull Bundle outState) { - outState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, - mContainerState == STATE_ANIMATING_OUT); - // In the case where biometric and credential are both allowed, we can assume that - // biometric isn't showing if credential is showing since biometric is shown first. - outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING, - mBiometricView != null && mCredentialView == null); - outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null); - - if (mBiometricView != null) { - mBiometricView.onSaveState(outState); - } - } - - @Override public String getOpPackageName() { return mConfig.mOpPackageName; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 5719fdd0b4de..b752c3b80cae 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -50,7 +50,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; -import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; @@ -966,7 +965,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, skipAnimation = true; } - showDialog(args, skipAnimation, null /* savedState */, mPromptViewModelProvider.get()); + showDialog(args, skipAnimation, mPromptViewModelProvider.get()); } /** @@ -1198,7 +1197,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, return mFpEnrolledForUser.getOrDefault(userId, false); } - private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState, + private void showDialog(SomeArgs args, boolean skipAnimation, @Nullable PromptViewModel viewModel) { mCurrentDialogArgs = args; @@ -1238,7 +1237,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, if (DEBUG) { Log.d(TAG, "userId: " + userId - + " savedState: " + savedState + " mCurrentDialog: " + mCurrentDialog + " newDialog: " + newDialog); } @@ -1260,7 +1258,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) { cancelIfOwnerIsNotInForeground(); } else { - mCurrentDialog.show(mWindowManager, savedState); + mCurrentDialog.show(mWindowManager); } } @@ -1282,29 +1280,12 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, public void onConfigurationChanged(Configuration newConfig) { updateSensorLocations(); - // Save the state of the current dialog (buttons showing, etc) + // TODO(b/287311775): consider removing this to retain the UI cleanly vs re-creating if (mCurrentDialog != null) { final PromptViewModel viewModel = mCurrentDialog.getViewModel(); - final Bundle savedState = new Bundle(); - mCurrentDialog.onSaveState(savedState); mCurrentDialog.dismissWithoutCallback(false /* animate */); mCurrentDialog = null; - - // Only show the dialog if necessary. If it was animating out, the dialog is supposed - // to send its pending callback immediately. - if (!savedState.getBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false)) { - final boolean credentialShowing = - savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING); - if (credentialShowing) { - // There may be a cleaner way to do this, rather than altering the current - // authentication's parameters. This gets the job done and should be clear - // enough for now. - PromptInfo promptInfo = (PromptInfo) mCurrentDialogArgs.arg1; - promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL); - } - - showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState, viewModel); - } + showDialog(mCurrentDialogArgs, true /* skipAnimation */, viewModel); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java index 3cfc6f280110..3fd488c34121 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java @@ -16,59 +16,20 @@ package com.android.systemui.biometrics; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.hardware.biometrics.BiometricAuthenticator.Modality; -import android.os.Bundle; import android.view.WindowManager; import com.android.systemui.Dumpable; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Interface for the biometric dialog UI. * - * TODO(b/251476085): remove along with legacy controller once flag is removed + * TODO(b/287311775): remove along with legacy controller once flag is removed */ @Deprecated public interface AuthDialog extends Dumpable { - String KEY_CONTAINER_GOING_AWAY = "container_going_away"; - String KEY_BIOMETRIC_SHOWING = "biometric_showing"; - String KEY_CREDENTIAL_SHOWING = "credential_showing"; - - String KEY_BIOMETRIC_CONFIRM_VISIBILITY = "confirm_visibility"; - String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility"; - String KEY_BIOMETRIC_STATE = "state"; - String KEY_BIOMETRIC_INDICATOR_STRING = "indicator_string"; // error / help / hint - String KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING = "error_is_temporary"; - String KEY_BIOMETRIC_INDICATOR_HELP_SHOWING = "hint_is_temporary"; - String KEY_BIOMETRIC_DIALOG_SIZE = "size"; - - String KEY_BIOMETRIC_SENSOR_TYPE = "sensor_type"; - String KEY_BIOMETRIC_SENSOR_PROPS = "sensor_props"; - - int SIZE_UNKNOWN = 0; - /** - * Minimal UI, showing only biometric icon. - */ - int SIZE_SMALL = 1; - /** - * Normal-sized biometric UI, showing title, icon, buttons, etc. - */ - int SIZE_MEDIUM = 2; - /** - * Full-screen credential UI. - */ - int SIZE_LARGE = 3; - @Retention(RetentionPolicy.SOURCE) - @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE}) - @interface DialogSize {} - /** * Parameters used when laying out {@link AuthBiometricView}, its subclasses, and * {@link AuthPanelController}. @@ -84,27 +45,10 @@ public interface AuthDialog extends Dumpable { } /** - * Animation duration, from small to medium dialog, including back panel, icon translation, etc - */ - int ANIMATE_SMALL_TO_MEDIUM_DURATION_MS = 150; - /** - * Animation duration from medium to large dialog, including biometric fade out, back panel, etc - */ - int ANIMATE_MEDIUM_TO_LARGE_DURATION_MS = 450; - /** - * Delay before notifying {@link AuthCredentialView} to start animating in. - */ - int ANIMATE_CREDENTIAL_START_DELAY_MS = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS * 2 / 3; - /** - * Animation duration when sliding in credential UI - */ - int ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150; - - /** * Show the dialog. * @param wm */ - void show(WindowManager wm, @Nullable Bundle savedState); + void show(WindowManager wm); /** * Dismiss the dialog without sending a callback. @@ -146,12 +90,6 @@ public interface AuthDialog extends Dumpable { void onPointerDown(); /** - * Save the current state. - * @param outState - */ - void onSaveState(@NonNull Bundle outState); - - /** * Get the client's package name */ String getOpPackageName(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt index f56bb881d8b7..958213afacdf 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt @@ -24,7 +24,7 @@ import android.graphics.drawable.Drawable import android.util.Log import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieCompositionFactory -import com.android.systemui.biometrics.AuthBiometricView.BiometricState +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState private const val TAG = "AuthIconController" @@ -76,7 +76,7 @@ abstract class AuthIconController( } /** Update the icon to reflect the [newState]. */ - fun updateState(@BiometricState lastState: Int, @BiometricState newState: Int) { + fun updateState(lastState: BiometricState, newState: BiometricState) { if (deactivated) { Log.w(TAG, "Ignoring updateState when deactivated: $newState") } else { @@ -85,7 +85,7 @@ abstract class AuthIconController( } /** Call during [updateState] if the controller is not [deactivated]. */ - abstract fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) + abstract fun updateIcon(lastState: BiometricState, newState: BiometricState) /** Called during [onAnimationEnd] if the controller is not [deactivated]. */ open fun handleAnimationEnd(drawable: Drawable) {} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 672f180c13b4..d616dcca338f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -23,7 +23,6 @@ import android.hardware.biometrics.BiometricAuthenticator import android.hardware.biometrics.BiometricConstants import android.hardware.biometrics.BiometricPrompt import android.hardware.face.FaceManager -import android.os.Bundle import android.text.method.ScrollingMovementMethod import android.util.Log import android.view.HapticFeedbackConstants @@ -43,9 +42,6 @@ import com.android.systemui.R import com.android.systemui.biometrics.AuthBiometricFaceIconController import com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceIconController import com.android.systemui.biometrics.AuthBiometricFingerprintIconController -import com.android.systemui.biometrics.AuthBiometricView -import com.android.systemui.biometrics.AuthBiometricView.Callback -import com.android.systemui.biometrics.AuthBiometricViewAdapter import com.android.systemui.biometrics.AuthIconController import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.shared.model.BiometricModalities @@ -63,7 +59,6 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.VibratorHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -83,11 +78,11 @@ object BiometricViewBinder { panelViewController: AuthPanelController, jankListener: BiometricJankListener, backgroundView: View, - legacyCallback: Callback, + legacyCallback: Spaghetti.Callback, applicationScope: CoroutineScope, vibratorHelper: VibratorHelper, featureFlags: FeatureFlags, - ): AuthBiometricViewAdapter { + ): Spaghetti { val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!! val textColorError = @@ -141,24 +136,20 @@ object BiometricViewBinder { subtitleView.text = viewModel.subtitle.first() // set button listeners - negativeButton.setOnClickListener { - legacyCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE) - } - cancelButton.setOnClickListener { - legacyCallback.onAction(Callback.ACTION_USER_CANCELED) - } + negativeButton.setOnClickListener { legacyCallback.onButtonNegative() } + cancelButton.setOnClickListener { legacyCallback.onUserCanceled() } credentialFallbackButton.setOnClickListener { viewModel.onSwitchToCredential() - legacyCallback.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL) + legacyCallback.onUseDeviceCredential() } confirmationButton.setOnClickListener { viewModel.confirmAuthenticated() } retryButton.setOnClickListener { viewModel.showAuthenticating(isRetry = true) - legacyCallback.onAction(Callback.ACTION_BUTTON_TRY_AGAIN) + legacyCallback.onButtonTryAgain() } // TODO(b/251476085): migrate legacy icon controllers and remove - var legacyState: Int = viewModel.legacyState.value + var legacyState = viewModel.legacyState.value val iconController = modalities.asIconController( view.context, @@ -219,7 +210,7 @@ object BiometricViewBinder { oldMode == FingerprintStartMode.Pending && newMode == FingerprintStartMode.Delayed ) { - legacyCallback.onAction(Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR) + legacyCallback.onStartDelayedFingerprintSensor() } if (newMode.isStarted) { @@ -246,7 +237,7 @@ object BiometricViewBinder { .collect { dismissOnClick -> backgroundView.setOnClickListener { if (dismissOnClick) { - legacyCallback.onAction(Callback.ACTION_USER_CANCELED) + legacyCallback.onUserCanceled() } else { Log.w(TAG, "Ignoring background click") } @@ -360,13 +351,11 @@ object BiometricViewBinder { launch { delay(authState.delay) - legacyCallback.onAction( - if (authState.isAuthenticatedAndExplicitlyConfirmed) { - Callback.ACTION_AUTHENTICATED_AND_CONFIRMED - } else { - Callback.ACTION_AUTHENTICATED - } - ) + if (authState.isAuthenticatedAndExplicitlyConfirmed) { + legacyCallback.onAuthenticatedAndConfirmed() + } else { + legacyCallback.onAuthenticated() + } } } } @@ -428,21 +417,50 @@ object BiometricViewBinder { * the view model (which will be retained) via the application scope. * * Do not reference the [view] for anything other than [asView]. - * - * TODO(b/251476085): remove after replacing AuthContainerView */ -private class Spaghetti( +@Deprecated("TODO(b/251476085): remove after replacing AuthContainerView") +class Spaghetti( private val view: View, private val viewModel: PromptViewModel, private val applicationContext: Context, private val applicationScope: CoroutineScope, -) : AuthBiometricViewAdapter { +) { + + @Deprecated("TODO(b/251476085): remove after replacing AuthContainerView") + interface Callback { + fun onAuthenticated() + fun onUserCanceled() + fun onButtonNegative() + fun onButtonTryAgain() + fun onError() + fun onUseDeviceCredential() + fun onStartDelayedFingerprintSensor() + fun onAuthenticatedAndConfirmed() + } + + @Deprecated("TODO(b/251476085): remove after replacing AuthContainerView") + enum class BiometricState { + /** Authentication hardware idle. */ + STATE_IDLE, + /** UI animating in, authentication hardware active. */ + STATE_AUTHENTICATING_ANIMATING_IN, + /** UI animated in, authentication hardware active. */ + STATE_AUTHENTICATING, + /** UI animated in, authentication hardware active. */ + STATE_HELP, + /** Hard error, e.g. ERROR_TIMEOUT. Authentication hardware idle. */ + STATE_ERROR, + /** Authenticated, waiting for user confirmation. Authentication hardware idle. */ + STATE_PENDING_CONFIRMATION, + /** Authenticated, dialog animating away soon. */ + STATE_AUTHENTICATED, + } private var lifecycleScope: CoroutineScope? = null private var modalities: BiometricModalities = BiometricModalities() private var legacyCallback: Callback? = null - override var legacyIconController: AuthIconController? = null + var legacyIconController: AuthIconController? = null private set // hacky way to suppress lockout errors @@ -478,7 +496,7 @@ private class Spaghetti( ) } - override fun onDialogAnimatedIn(fingerprintWasStarted: Boolean) { + fun onDialogAnimatedIn(fingerprintWasStarted: Boolean) { if (fingerprintWasStarted) { viewModel.ensureFingerprintHasStarted(isDelayed = false) viewModel.showAuthenticating(modalities.asDefaultHelpMessage(applicationContext)) @@ -487,7 +505,7 @@ private class Spaghetti( } } - override fun onAuthenticationSucceeded(@BiometricAuthenticator.Modality modality: Int) { + fun onAuthenticationSucceeded(@BiometricAuthenticator.Modality modality: Int) { applicationScope.launch { val authenticatedModality = modality.asBiometricModality() val msgId = getHelpForSuccessfulAuthentication(authenticatedModality) @@ -511,7 +529,7 @@ private class Spaghetti( else -> null } - override fun onAuthenticationFailed( + fun onAuthenticationFailed( @BiometricAuthenticator.Modality modality: Int, failureReason: String, ) { @@ -533,7 +551,7 @@ private class Spaghetti( } } - override fun onError(modality: Int, error: String) { + fun onError(modality: Int, error: String) { val errorModality = modality.asBiometricModality() if (ignoreUnsuccessfulEventsFrom(errorModality, error)) { return @@ -546,11 +564,11 @@ private class Spaghetti( authenticateAfterError = modalities.hasFingerprint, ) delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong()) - legacyCallback?.onAction(Callback.ACTION_ERROR) + legacyCallback?.onError() } } - override fun onHelp(modality: Int, help: String) { + fun onHelp(modality: Int, help: String) { if (ignoreUnsuccessfulEventsFrom(modality.asBiometricModality(), "")) { return } @@ -574,36 +592,20 @@ private class Spaghetti( else -> false } - override fun startTransitionToCredentialUI(isError: Boolean) { + fun startTransitionToCredentialUI(isError: Boolean) { applicationScope.launch { viewModel.onSwitchToCredential() - legacyCallback?.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL) + legacyCallback?.onUseDeviceCredential() } } - override fun requestLayout() { - // nothing, for legacy view... - } - - override fun restoreState(bundle: Bundle?) { - // nothing, for legacy view... - } - - override fun onSaveState(bundle: Bundle?) { - // nothing, for legacy view... - } - - override fun onOrientationChanged() { - // nothing, for legacy view... - } - - override fun cancelAnimation() { + fun cancelAnimation() { view.animate()?.cancel() } - override fun isCoex() = modalities.hasFaceAndFingerprint + fun isCoex() = modalities.hasFaceAndFingerprint - override fun asView() = view + fun asView() = view } private fun BiometricModalities.asDefaultHelpMessage(context: Context): String = @@ -638,7 +640,7 @@ private class HackyCoexIconController( iconViewOverlay: LottieAnimationView, ) : AuthBiometricFingerprintAndFaceIconController(context, iconView, iconViewOverlay) { - private var state: Int? = null + private var state: Spaghetti.BiometricState? = null private val faceController = AuthBiometricFaceIconController(context, iconView) var faceMode: Boolean = true @@ -649,11 +651,14 @@ private class HackyCoexIconController( faceController.deactivated = !value iconView.setImageIcon(null) iconViewOverlay.setImageIcon(null) - state?.let { updateIcon(AuthBiometricView.STATE_IDLE, it) } + state?.let { updateIcon(Spaghetti.BiometricState.STATE_IDLE, it) } } } - override fun updateIcon(lastState: Int, newState: Int) { + override fun updateIcon( + lastState: Spaghetti.BiometricState, + newState: Spaghetti.BiometricState, + ) { if (deactivated) { return } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 370b36bcaec2..b9af03166fed 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -31,7 +31,6 @@ import androidx.core.view.doOnLayout import androidx.core.view.isGone import androidx.lifecycle.lifecycleScope import com.android.systemui.R -import com.android.systemui.biometrics.AuthDialog import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.ui.BiometricPromptLayout @@ -47,6 +46,10 @@ import kotlinx.coroutines.launch /** Helper for [BiometricViewBinder] to handle resize transitions. */ object BiometricViewSizeBinder { + private const val ANIMATE_SMALL_TO_MEDIUM_DURATION_MS = 150 + // TODO(b/201510778): make private when related misuse is fixed + const val ANIMATE_MEDIUM_TO_LARGE_DURATION_MS = 450 + /** Resizes [BiometricPromptLayout] and the [panelViewController] via the [PromptViewModel]. */ fun bind( view: BiometricPromptLayout, @@ -134,7 +137,7 @@ object BiometricViewSizeBinder { ) } size.isMedium && currentSize.isSmall -> { - val duration = AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS + val duration = ANIMATE_SMALL_TO_MEDIUM_DURATION_MS panelViewController.updateForContentDimensions( width, height, @@ -165,7 +168,7 @@ object BiometricViewSizeBinder { ) } size.isLarge -> { - val duration = AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS + val duration = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS panelViewController.setUseFullScreen(true) panelViewController.updateForContentDimensions( panelViewController.containerWidth, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt index 25fe61916644..931946aaa382 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt @@ -9,7 +9,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.systemui.R -import com.android.systemui.biometrics.AuthDialog import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.ui.CredentialPasswordView import com.android.systemui.biometrics.ui.CredentialPatternView @@ -22,6 +21,8 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +private const val ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150 + /** * View binder for all credential variants of BiometricPrompt, including [CredentialPatternView] and * [CredentialPasswordView]. @@ -147,7 +148,7 @@ private fun View.animateCredentialViewIn() { postOnAnimation { animate() .translationY(0f) - .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS.toLong()) + .setDuration(ANIMATE_CREDENTIAL_INITIAL_DURATION_MS.toLong()) .alpha(1f) .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) .withLayer() diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index fbc88d43c48e..6269700d5419 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -19,12 +19,12 @@ import android.hardware.biometrics.BiometricPrompt import android.util.Log import android.view.HapticFeedbackConstants import android.view.MotionEvent -import com.android.systemui.biometrics.AuthBiometricView import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.biometrics.ui.binder.Spaghetti import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.statusbar.VibratorHelper @@ -62,8 +62,8 @@ constructor( .distinctUntilChanged() // TODO(b/251476085): remove after icon controllers are migrated - do not keep this state - private var _legacyState = MutableStateFlow(AuthBiometricView.STATE_IDLE) - val legacyState: StateFlow<Int> = _legacyState.asStateFlow() + private var _legacyState = MutableStateFlow(Spaghetti.BiometricState.STATE_IDLE) + val legacyState: StateFlow<Spaghetti.BiometricState> = _legacyState.asStateFlow() private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false) @@ -270,7 +270,7 @@ constructor( _isAuthenticated.value = PromptAuthState(false) _forceMediumSize.value = true _message.value = PromptMessage.Error(message) - _legacyState.value = AuthBiometricView.STATE_ERROR + _legacyState.value = Spaghetti.BiometricState.STATE_ERROR if (hapticFeedback) { vibrator.error(failedModality) @@ -322,13 +322,13 @@ constructor( _forceMediumSize.value = true _legacyState.value = if (alreadyAuthenticated && isConfirmationRequired.first()) { - AuthBiometricView.STATE_PENDING_CONFIRMATION + Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION } else if (alreadyAuthenticated && !isConfirmationRequired.first()) { - AuthBiometricView.STATE_AUTHENTICATED + Spaghetti.BiometricState.STATE_AUTHENTICATED } else if (clearIconError) { - AuthBiometricView.STATE_IDLE + Spaghetti.BiometricState.STATE_IDLE } else { - AuthBiometricView.STATE_HELP + Spaghetti.BiometricState.STATE_HELP } messageJob?.cancel() @@ -353,7 +353,7 @@ constructor( _message.value = if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty _forceMediumSize.value = true - _legacyState.value = AuthBiometricView.STATE_HELP + _legacyState.value = Spaghetti.BiometricState.STATE_HELP messageJob?.cancel() messageJob = launch { @@ -373,7 +373,7 @@ constructor( _isAuthenticating.value = true _isAuthenticated.value = PromptAuthState(false) _message.value = if (message.isBlank()) PromptMessage.Empty else PromptMessage.Help(message) - _legacyState.value = AuthBiometricView.STATE_AUTHENTICATING + _legacyState.value = Spaghetti.BiometricState.STATE_AUTHENTICATING // reset the try again button(s) after the user attempts a retry if (isRetry) { @@ -406,9 +406,9 @@ constructor( _message.value = PromptMessage.Empty _legacyState.value = if (needsUserConfirmation) { - AuthBiometricView.STATE_PENDING_CONFIRMATION + Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION } else { - AuthBiometricView.STATE_AUTHENTICATED + Spaghetti.BiometricState.STATE_AUTHENTICATED } if (!needsUserConfirmation) { @@ -449,7 +449,7 @@ constructor( _isAuthenticated.value = authState.asExplicitlyConfirmed() _message.value = PromptMessage.Empty - _legacyState.value = AuthBiometricView.STATE_AUTHENTICATED + _legacyState.value = Spaghetti.BiometricState.STATE_AUTHENTICATED vibrator.success(authState.authenticatedModality) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 595e0a300177..4cb9622ab4c9 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -659,8 +659,6 @@ object Flags { // TODO(b/259264861): Tracking Bug @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection") @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag("udfps_ellipse_detection") - // TODO(b/278622168): Tracking Bug - @JvmField val BIOMETRIC_BP_STRONG = releasedFlag("biometric_bp_strong") // 2300 - stylus @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag("track_stylus_ever_used") diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt deleted file mode 100644 index a93af7dd7450..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE -import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT -import android.hardware.biometrics.BiometricConstants -import android.hardware.face.FaceManager -import android.testing.TestableLooper -import android.testing.TestableLooper.RunWithLooper -import android.view.View -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.RoboPilotTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.Mockito.times -import org.mockito.junit.MockitoJUnit - - -@RunWith(AndroidJUnit4::class) -@RunWithLooper(setAsMainLooper = true) -@SmallTest -@RoboPilotTest -class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { - - @JvmField - @Rule - var mockitoRule = MockitoJUnit.rule() - - @Mock - private lateinit var callback: AuthBiometricView.Callback - - @Mock - private lateinit var panelController: AuthPanelController - - private lateinit var biometricView: AuthBiometricFingerprintAndFaceView - - @Before - fun setup() { - biometricView = R.layout.auth_biometric_fingerprint_and_face_view - .asTestAuthBiometricView(mContext, callback, panelController) - waitForIdleSync() - } - - @After - fun tearDown() { - biometricView.destroyDialog() - } - - @Test - fun fingerprintSuccessDoesNotRequireExplicitConfirmation() { - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT) - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) - } - - @Test - fun faceSuccessRequiresExplicitConfirmation() { - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.onAuthenticationSucceeded(TYPE_FACE) - waitForIdleSync() - - assertThat(biometricView.isAuthenticated).isFalse() - assertThat(biometricView.isAuthenticating).isFalse() - assertThat(biometricView.mConfirmButton.visibility).isEqualTo(View.GONE) - verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) - - // icon acts as confirm button - biometricView.mIconView.performClick() - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED) - } - - @Test - fun ignoresFaceErrors_faceIsNotClass3_notLockoutError() { - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.onError(TYPE_FACE, "not a face") - waitForIdleSync() - - assertThat(biometricView.isAuthenticating).isTrue() - verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR) - - biometricView.onError(TYPE_FINGERPRINT, "that's a nope") - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) - } - - @Test - fun doNotIgnoresFaceErrors_faceIsClass3_notLockoutError() { - biometricView.isFaceClass3 = true - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.onError(TYPE_FACE, "not a face") - waitForIdleSync() - - assertThat(biometricView.isAuthenticating).isTrue() - verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR) - - biometricView.onError(TYPE_FINGERPRINT, "that's a nope") - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) - } - - @Test - fun doNotIgnoresFaceErrors_faceIsClass3_lockoutError() { - biometricView.isFaceClass3 = true - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.onError( - TYPE_FACE, - FaceManager.getErrorString( - biometricView.context, - BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT, - 0 /*vendorCode */ - ) - ) - waitForIdleSync() - - assertThat(biometricView.isAuthenticating).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR) - - biometricView.onError(TYPE_FINGERPRINT, "that's a nope") - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback, times(2)).onAction(AuthBiometricView.Callback.ACTION_ERROR) - } - - - override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages() -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt index cac618b21dc7..52bf350bfcc8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt @@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -63,7 +64,7 @@ class AuthBiometricFingerprintIconControllerTest : SysuiTestCase() { setupFingerprintSensorProperties(FingerprintSensorProperties.TYPE_POWER_BUTTON) controller = AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) - assertThat(controller.getIconContentDescription(AuthBiometricView.STATE_AUTHENTICATING)) + assertThat(controller.getIconContentDescription(BiometricState.STATE_AUTHENTICATING)) .isEqualTo( context.resources.getString( R.string.security_settings_sfps_enroll_find_sensor_message @@ -76,7 +77,7 @@ class AuthBiometricFingerprintIconControllerTest : SysuiTestCase() { setupFingerprintSensorProperties(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) controller = AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) - assertThat(controller.getIconContentDescription(AuthBiometricView.STATE_AUTHENTICATING)) + assertThat(controller.getIconContentDescription(BiometricState.STATE_AUTHENTICATING)) .isEqualTo(context.resources.getString(R.string.fingerprint_dialog_touch_sensor)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt deleted file mode 100644 index 8e5d96b0a2c6..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.hardware.biometrics.BiometricAuthenticator -import android.os.Bundle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import android.testing.TestableLooper -import android.testing.TestableLooper.RunWithLooper -import android.view.View -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.RoboPilotTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit - -@RunWith(AndroidJUnit4::class) -@RunWithLooper(setAsMainLooper = true) -@SmallTest -@RoboPilotTest -class AuthBiometricFingerprintViewTest : SysuiTestCase() { - - @JvmField - @Rule - val mockitoRule = MockitoJUnit.rule() - - @Mock - private lateinit var callback: AuthBiometricView.Callback - - @Mock - private lateinit var panelController: AuthPanelController - - private lateinit var biometricView: AuthBiometricView - - private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView { - val view: AuthBiometricFingerprintView = - R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView( - mContext, callback, panelController, allowDeviceCredential = allowDeviceCredential - ) - waitForIdleSync() - return view - } - - @Before - fun setup() { - biometricView = createView() - } - - @After - fun tearDown() { - biometricView.destroyDialog() - } - - @Test - fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() { - biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) - } - - @Test - fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() { - biometricView.setRequireConfirmation(true) - biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - // TODO: this should be tested in the subclasses - if (biometricView.supportsRequireConfirmation()) { - verify(callback, never()).onAction(ArgumentMatchers.anyInt()) - assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE) - assertThat(biometricView.mCancelButton.visibility).isEqualTo(View.VISIBLE) - assertThat(biometricView.mCancelButton.isEnabled).isTrue() - assertThat(biometricView.mConfirmButton.isEnabled).isTrue() - assertThat(biometricView.mIndicatorView.text) - .isEqualTo(mContext.getText(R.string.biometric_dialog_tap_confirm)) - assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE) - } else { - assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_AUTHENTICATED)) - } - } - - @Test - fun testPositiveButton_sendsActionAuthenticated() { - biometricView.mConfirmButton.performClick() - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) - assertThat(biometricView.isAuthenticated).isTrue() - } - - @Test - fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() { - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - biometricView.mNegativeButton.performClick() - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE) - } - - @Test - fun testCancelButton_whenPendingConfirmation_sendsActionUserCanceled() { - biometricView.setRequireConfirmation(true) - biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) - - assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE) - biometricView.mCancelButton.performClick() - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED) - } - - @Test - fun testTryAgainButton_sendsActionTryAgain() { - biometricView.mTryAgainButton.performClick() - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN) - assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE) - assertThat(biometricView.isAuthenticating).isTrue() - } - - @Test - fun testOnErrorSendsActionError() { - biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError") - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR)) - } - - @Test - fun testOnErrorShowsMessage() { - // prevent error state from instantly returning to authenticating in the test - biometricView.mAnimationDurationHideDialog = 10_000 - - val message = "another error" - biometricView.onError(BiometricAuthenticator.TYPE_FACE, message) - TestableLooper.get(this).moveTimeForward(1000) - waitForIdleSync() - - assertThat(biometricView.isAuthenticating).isFalse() - assertThat(biometricView.isAuthenticated).isFalse() - assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE) - assertThat(biometricView.mIndicatorView.text).isEqualTo(message) - } - - @Test - fun testBackgroundClicked_sendsActionUserCanceled() { - val view = View(mContext) - biometricView.setBackgroundView(view) - view.performClick() - - verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED)) - } - - @Test - fun testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() { - val view = View(mContext) - biometricView.setBackgroundView(view) - biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) - waitForIdleSync() - view.performClick() - - verify(callback, never()) - .onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED)) - } - - @Test - fun testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() { - biometricView.mLayoutParams = AuthDialog.LayoutParams(0, 0) - biometricView.updateSize(AuthDialog.SIZE_SMALL) - val view = View(mContext) - biometricView.setBackgroundView(view) - view.performClick() - - verify(callback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED)) - } - - @Test - fun testIgnoresUselessHelp() { - biometricView.mAnimationDurationHideDialog = 10_000 - biometricView.onDialogAnimatedIn(fingerprintWasStarted = true) - waitForIdleSync() - - assertThat(biometricView.isAuthenticating).isTrue() - - val helpText = biometricView.mIndicatorView.text - biometricView.onHelp(BiometricAuthenticator.TYPE_FINGERPRINT, "") - waitForIdleSync() - - // text should not change - assertThat(biometricView.mIndicatorView.text).isEqualTo(helpText) - verify(callback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR)) - } - - @Test - fun testRestoresState() { - val requireConfirmation = true - biometricView.mAnimationDurationHideDialog = 10_000 - val failureMessage = "testFailureMessage" - biometricView.setRequireConfirmation(requireConfirmation) - biometricView.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, failureMessage) - waitForIdleSync() - - val state = Bundle() - biometricView.onSaveState(state) - assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE) - assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY)) - .isEqualTo(View.GONE) - assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE)) - .isEqualTo(AuthBiometricView.STATE_ERROR) - assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE) - assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue() - assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage) - assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING)) - .isEqualTo(failureMessage) - - // TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle - - // Create new dialog and restore the previous state into it - biometricView.destroyDialog() - biometricView = createView() - biometricView.restoreState(state) - biometricView.mAnimationDurationHideDialog = 10_000 - biometricView.setRequireConfirmation(requireConfirmation) - waitForIdleSync() - - assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE) - assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE) - - // TODO: Test restored text. Currently cannot test this, since it gets restored only after - // dialog size is known. - } - - @Test - fun testCredentialButton_whenDeviceCredentialAllowed() { - biometricView.destroyDialog() - biometricView = createView(allowDeviceCredential = true) - - assertThat(biometricView.mUseCredentialButton.visibility).isEqualTo(View.VISIBLE) - assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE) - - biometricView.mUseCredentialButton.performClick() - waitForIdleSync() - - verify(callback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL) - } - - override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages() -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index d10b81c7b648..7775a05568e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -105,9 +105,6 @@ open class AuthContainerViewTest : SysuiTestCase() { @Mock lateinit var vibrator: VibratorHelper - // TODO(b/278622168): remove with flag - open val useNewBiometricPrompt = false - private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val biometricPromptRepository = FakePromptRepository() @@ -137,7 +134,6 @@ open class AuthContainerViewTest : SysuiTestCase() { @Before fun setup() { displayRepository = FakeDisplayRepository() - featureFlags.set(Flags.BIOMETRIC_BP_STRONG, useNewBiometricPrompt) featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false) displayStateInteractor = @@ -235,9 +231,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testActionCancel_panelInteractionDetectorDisable() { val container = initializeFingerprintContainer() - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_USER_CANCELED - ) + container.mBiometricCallback.onUserCanceled() waitForIdleSync() verify(panelInteractionDetector).disable() } @@ -246,9 +240,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testActionAuthenticated_sendsDismissedAuthenticated() { val container = initializeFingerprintContainer() - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_AUTHENTICATED - ) + container.mBiometricCallback.onAuthenticated() waitForIdleSync() verify(callback).onDismissed( @@ -262,9 +254,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testActionUserCanceled_sendsDismissedUserCanceled() { val container = initializeFingerprintContainer() - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_USER_CANCELED - ) + container.mBiometricCallback.onUserCanceled() waitForIdleSync() verify(callback).onSystemEvent( @@ -282,9 +272,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testActionButtonNegative_sendsDismissedButtonNegative() { val container = initializeFingerprintContainer() - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE - ) + container.mBiometricCallback.onButtonNegative() waitForIdleSync() verify(callback).onDismissed( @@ -300,9 +288,7 @@ open class AuthContainerViewTest : SysuiTestCase() { val container = initializeFingerprintContainer( authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK ) - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN - ) + container.mBiometricCallback.onButtonTryAgain() waitForIdleSync() verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L) @@ -311,9 +297,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testActionError_sendsDismissedError() { val container = initializeFingerprintContainer() - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_ERROR - ) + container.mBiometricCallback.onError() waitForIdleSync() verify(callback).onDismissed( @@ -331,9 +315,7 @@ open class AuthContainerViewTest : SysuiTestCase() { authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL ) - container.mBiometricCallback.onAction( - AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL - ) + container.mBiometricCallback.onUseDeviceCredential() waitForIdleSync() verify(callback).onDeviceCredentialPressed(authContainer?.requestId ?: 0L) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest2.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest2.kt deleted file mode 100644 index b56d05537215..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest2.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import org.junit.runner.RunWith - -// TODO(b/278622168): remove with flag -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@SmallTest -class AuthContainerViewTest2 : AuthContainerViewTest() { - override val useNewBiometricPrompt = true -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index f3ff669917ee..d0b3833a24a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -17,7 +17,6 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricManager.Authenticators; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -29,7 +28,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -45,7 +43,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.hardware.biometrics.BiometricAuthenticator; @@ -66,7 +63,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; -import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; @@ -91,7 +87,6 @@ import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteracto import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.VibratorHelper; @@ -203,10 +198,6 @@ public class AuthControllerTest extends SysuiTestCase { @Before public void setup() throws RemoteException { - // TODO(b/278622168): remove with flag - // AuthController simply passes this through to AuthContainerView (does not impact test) - mFeatureFlags.set(Flags.BIOMETRIC_BP_STRONG, false); - mContextSpy = spy(mContext); mExecution = new FakeExecution(); mTestableLooper = TestableLooper.get(this); @@ -459,7 +450,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testShowInvoked_whenSystemRequested() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - verify(mDialog1).show(any(), any()); + verify(mDialog1).show(any()); } @Test @@ -660,7 +651,7 @@ public class AuthControllerTest extends SysuiTestCase { // 2) Client cancels authentication showDialog(new int[0] /* sensorIds */, true /* credentialAllowed */); - verify(mDialog1).show(any(), any()); + verify(mDialog1).show(any()); final byte[] credentialAttestation = generateRandomHAT(); @@ -676,7 +667,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - verify(mDialog1).show(any(), any()); + verify(mDialog1).show(any()); showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); @@ -684,59 +675,7 @@ public class AuthControllerTest extends SysuiTestCase { verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */); // Second dialog should be shown without animation - verify(mDialog2).show(any(), any()); - } - - @Test - public void testConfigurationPersists_whenOnConfigurationChanged() { - showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - verify(mDialog1).show(any(), any()); - - // Return that the UI is in "showing" state - doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - Bundle savedState = (Bundle) args[0]; - savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false); - return null; // onSaveState returns void - }).when(mDialog1).onSaveState(any()); - - mAuthController.onConfigurationChanged(new Configuration()); - - ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); - verify(mDialog1).onSaveState(captor.capture()); - - // Old dialog doesn't animate - verify(mDialog1).dismissWithoutCallback(eq(false /* animate */)); - - // Saved state is restored into new dialog - ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class); - verify(mDialog2).show(any(), captor2.capture()); - - // TODO: This should check all values we want to save/restore - assertEquals(captor.getValue(), captor2.getValue()); - } - - @Test - public void testConfigurationPersists_whenBiometricFallbackToCredential() { - showDialog(new int[] {1} /* sensorIds */, true /* credentialAllowed */); - verify(mDialog1).show(any(), any()); - - // Pretend that the UI is now showing device credential UI. - doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - Bundle savedState = (Bundle) args[0]; - savedState.putBoolean(AuthDialog.KEY_CONTAINER_GOING_AWAY, false); - savedState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, true); - return null; // onSaveState returns void - }).when(mDialog1).onSaveState(any()); - - mAuthController.onConfigurationChanged(new Configuration()); - - // Check that the new dialog was initialized to the credential UI. - ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); - verify(mDialog2).show(any(), captor.capture()); - assertEquals(Authenticators.DEVICE_CREDENTIAL, - mAuthController.mLastBiometricPromptInfo.getAuthenticators()); + verify(mDialog2).show(any()); } @Test @@ -1006,7 +945,7 @@ public class AuthControllerTest extends SysuiTestCase { REQUEST_ID); assertNull(mAuthController.mCurrentDialog); - verify(mDialog1, never()).show(any(), any()); + verify(mDialog1, never()).show(any()); } private void showDialog(int[] sensorIds, boolean credentialAllowed) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt index 94244cdf271d..9f24a9f553a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt @@ -16,8 +16,6 @@ package com.android.systemui.biometrics -import android.annotation.IdRes -import android.content.Context import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.ComponentInfoInternal import android.hardware.biometrics.PromptInfo @@ -27,57 +25,6 @@ import android.hardware.face.FaceSensorProperties import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.os.Bundle -import android.testing.ViewUtils -import android.view.LayoutInflater - -/** - * Inflate the given BiometricPrompt layout and initialize it with test parameters. - * - * This attaches the view so be sure to call [destroyDialog] at the end of the test. - */ -@IdRes -internal fun <T : AuthBiometricView> Int.asTestAuthBiometricView( - context: Context, - callback: AuthBiometricView.Callback, - panelController: AuthPanelController, - allowDeviceCredential: Boolean = false, - savedState: Bundle? = null, - hideDelay: Int = 0 -): T { - val view = LayoutInflater.from(context).inflate(this, null, false) as T - view.mAnimationDurationLong = 0 - view.mAnimationDurationShort = 0 - view.mAnimationDurationHideDialog = hideDelay - view.setPromptInfo(buildPromptInfo(allowDeviceCredential)) - view.setCallback(callback) - view.restoreState(savedState) - view.setPanelController(panelController) - - ViewUtils.attachView(view) - - return view -} - -private fun buildPromptInfo(allowDeviceCredential: Boolean): PromptInfo { - val promptInfo = PromptInfo() - promptInfo.title = "Title" - var authenticators = Authenticators.BIOMETRIC_WEAK - if (allowDeviceCredential) { - authenticators = authenticators or Authenticators.DEVICE_CREDENTIAL - } else { - promptInfo.negativeButtonText = "Negative" - } - promptInfo.authenticators = authenticators - return promptInfo -} - -/** Detach the view, if needed. */ -internal fun AuthBiometricView?.destroyDialog() { - if (this != null && isAttachedToWindow) { - ViewUtils.detachView(this) - } -} /** Create [FingerprintSensorPropertiesInternal] for a test. */ internal fun fingerprintSensorPropertiesInternal( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 3848aad695ab..5834e31cb591 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -24,7 +24,6 @@ import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.AuthBiometricView import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository @@ -37,6 +36,7 @@ import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality +import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.display.data.repository.FakeDisplayRepository @@ -132,7 +132,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } assertThat(message).isEqualTo(PromptMessage.Empty) assertThat(size).isEqualTo(expectedSize) - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_IDLE) + assertThat(legacyState).isEqualTo(BiometricState.STATE_IDLE) val startMessage = "here we go" viewModel.showAuthenticating(startMessage, isRetry = false) @@ -142,7 +142,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(authenticated?.isNotAuthenticated).isTrue() assertThat(size).isEqualTo(expectedSize) assertButtonsVisible(negative = expectedSize != PromptSize.SMALL) - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATING) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATING) } @Test @@ -220,7 +220,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(authenticating).isTrue() assertThat(authenticated?.isNotAuthenticated).isTrue() assertThat(size).isEqualTo(if (authWithSmallPrompt) PromptSize.SMALL else PromptSize.MEDIUM) - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATING) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATING) assertButtonsVisible(negative = !authWithSmallPrompt) val delay = 1000L @@ -240,9 +240,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(legacyState) .isEqualTo( if (expectConfirmation) { - AuthBiometricView.STATE_PENDING_CONFIRMATION + BiometricState.STATE_PENDING_CONFIRMATION } else { - AuthBiometricView.STATE_AUTHENTICATED + BiometricState.STATE_AUTHENTICATED } ) assertButtonsVisible( @@ -311,7 +311,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(size).isEqualTo(PromptSize.MEDIUM) assertThat(message).isEqualTo(PromptMessage.Error(errorMessage)) assertThat(messageVisible).isTrue() - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_ERROR) + assertThat(legacyState).isEqualTo(BiometricState.STATE_ERROR) // temporary error should disappear after a delay errorJob.join() @@ -326,11 +326,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(legacyState) .isEqualTo( if (restart) { - AuthBiometricView.STATE_AUTHENTICATING + BiometricState.STATE_AUTHENTICATING } else if (clearIconError) { - AuthBiometricView.STATE_IDLE + BiometricState.STATE_IDLE } else { - AuthBiometricView.STATE_HELP + BiometricState.STATE_HELP } ) @@ -505,7 +505,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(authenticating).isFalse() assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED) assertThat(canTryAgain).isFalse() } @@ -531,7 +531,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(authenticated?.isAuthenticated).isTrue() if (testCase.isFaceOnly && expectConfirmation) { - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION) + assertThat(legacyState).isEqualTo(BiometricState.STATE_PENDING_CONFIRMATION) assertThat(size).isEqualTo(PromptSize.MEDIUM) assertButtonsVisible( @@ -543,7 +543,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(message).isEqualTo(PromptMessage.Empty) assertButtonsVisible() } else { - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED) } } @@ -580,7 +580,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(authenticating).isFalse() assertThat(authenticated?.isAuthenticated).isTrue() - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED) assertThat(canTryAgain).isFalse() } @@ -614,7 +614,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa viewModel.showHelp(helpMessage) assertThat(size).isEqualTo(PromptSize.MEDIUM) - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_HELP) + assertThat(legacyState).isEqualTo(BiometricState.STATE_HELP) assertThat(message).isEqualTo(PromptMessage.Help(helpMessage)) assertThat(messageVisible).isTrue() @@ -642,9 +642,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(size).isEqualTo(PromptSize.MEDIUM) if (confirmationRequired == true) { - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION) + assertThat(legacyState).isEqualTo(BiometricState.STATE_PENDING_CONFIRMATION) } else { - assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED) + assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED) } assertThat(message).isEqualTo(PromptMessage.Help(helpMessage)) assertThat(messageVisible).isTrue() |