2/n: Add default implementation for multi-biometric enroll
1) Adds a layout for multi-biometric selection in BiometricEnrollActivity 2) Adds widgets for checkboxes 3) Shows ConfirmLock*/ChooseLock* for multi-biometric devices in BiometricEnrollActivity 4) finish()'s when loses foreground 5) Adds default string for ChooseLock* and multi-biometrics, e.g. "Set up Password + Biometrics", as well as associated plumbing to bring the user back to BiometricEnrollActivity once the credential is enrolled 6) When max templates enrolled, checkbox becomes disabled and description string is updated Bug: 162341940 Bug: 152242790 Fixes: 161742393 No effect on existing devices with the following: Test: adb shell am start -a android.settings.BIOMETRIC_ENROLL Test: SUW Test: make -j RunSettingsRoboTests Exempt-From-Owner-Approval: Biometric-related change to EncryptionInterstitial Change-Id: I855460d50228ace24d4ec5fbe330f02ab406cc02
This commit is contained in:
@@ -14,13 +14,12 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M10.25,13c0,0.69-0.56,1.25-1.25,1.25S7.75,13.69,7.75,13S8.31,11.75,9,11.75S10.25,12.31,10.25,13z M15,11.75 c-0.69,0-1.25,0.56-1.25,1.25s0.56,1.25,1.25,1.25s1.25-0.56,1.25-1.25S15.69,11.75,15,11.75z M22,12c0,5.52-4.48,10-10,10 S2,17.52,2,12S6.48,2,12,2S22,6.48,22,12z M20,12c0-0.78-0.12-1.53-0.33-2.24C18.97,9.91,18.25,10,17.5,10 c-3.13,0-5.92-1.44-7.76-3.69c-1.05,2.56-3.14,4.57-5.74,5.55C4.01,11.9,4,11.95,4,12c0,4.41,3.59,8,8,8S20,16.41,20,12z" />
|
||||
|
64
res/layout/biometric_enroll_checkbox.xml
Normal file
64
res/layout/biometric_enroll_checkbox.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- Icon -->
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<!-- Title -->
|
||||
<com.google.android.setupdesign.view.RichTextView
|
||||
style="@style/SudDescription.Glif"
|
||||
android:id="@+id/title"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingTop="16dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="start"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_toStartOf="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Description -->
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_toStartOf="@+id/checkbox"
|
||||
android:layout_below="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Check Box -->
|
||||
<CheckBox
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_centerVertical="true"
|
||||
android:checked="true"/>
|
||||
|
||||
</RelativeLayout>
|
80
res/layout/biometric_enroll_layout.xml
Normal file
80
res/layout/biometric_enroll_layout.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<com.google.android.setupdesign.GlifLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:BiometricEnrollCheckbox="http://schemas.android.com/apk/res/com.android.settings"
|
||||
style="?attr/face_layout_theme"
|
||||
app:sucHeaderText="@string/multi_biometric_enroll_title"
|
||||
android:id="@+id/setup_wizard_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SudContentFrame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.setupdesign.view.RichTextView
|
||||
android:id="@+id/sud_layout_description"
|
||||
style="@style/SudDescription.Glif"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multi_biometric_enroll_subtitle"/>
|
||||
|
||||
<com.google.android.setupdesign.view.IllustrationVideoView
|
||||
android:id="@+id/illustration_normal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/SudContentIllustration"
|
||||
app:sudVideo="@raw/face_settings"/>
|
||||
|
||||
<TextView
|
||||
style="@style/SudDescription.Glif"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/setup_with"
|
||||
android:text="@string/multi_biometric_enroll_setup_with"
|
||||
android:gravity="start"/>
|
||||
|
||||
<include layout="@layout/horizontal_divider"/>
|
||||
|
||||
<com.android.settings.biometrics.BiometricEnrollCheckbox
|
||||
android:id="@+id/checkbox_face"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
BiometricEnrollCheckbox:title="@string/multi_biometric_enroll_face_unlock_title"
|
||||
BiometricEnrollCheckbox:description="@string/multi_biometric_enroll_face_unlock_description"
|
||||
BiometricEnrollCheckbox:icon="@drawable/ic_face_24dp"/>
|
||||
|
||||
<include layout="@layout/horizontal_divider"/>
|
||||
|
||||
<com.android.settings.biometrics.BiometricEnrollCheckbox
|
||||
android:id="@+id/checkbox_fingerprint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
BiometricEnrollCheckbox:title="@string/multi_biometric_enroll_fingerprint_unlock_title"
|
||||
BiometricEnrollCheckbox:description="@string/multi_biometric_enroll_fingerprint_unlock_description"
|
||||
BiometricEnrollCheckbox:icon="@drawable/ic_fingerprint_24dp"/>
|
||||
|
||||
<include layout="@layout/horizontal_divider"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.setupdesign.GlifLayout>
|
@@ -16,12 +16,12 @@
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/face_header_description"
|
||||
android:id="@+id/biometric_header_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="56dp"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:text="@string/lock_settings_picker_face_message"
|
||||
style="@style/FaceHeaderStyle" />
|
||||
android:text="@string/lock_settings_picker_biometric_message"
|
||||
style="@style/BiometricHeaderStyle" />
|
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fingerprint_header_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="56dp"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:text="@string/lock_settings_picker_fingerprint_message"
|
||||
style="@style/FingerprintHeaderStyle" />
|
@@ -148,6 +148,13 @@
|
||||
<attr name="thickness" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- For biometric enroll checkboxes -->
|
||||
<declare-styleable name="BiometricEnrollCheckbox">
|
||||
<attr name="icon" format="reference" />
|
||||
<attr name="title" format="reference" />
|
||||
<attr name="description" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- For Face enroll accessibility toggle -->
|
||||
<declare-styleable name="FaceEnrollAccessibilityToggle">
|
||||
<attr name="messageText" format="reference" />
|
||||
|
@@ -711,6 +711,26 @@
|
||||
<!-- Summary for Security settings when fingerprint is not supported [CHAR LIMIT=NONE]-->
|
||||
<string name="security_dashboard_summary_no_fingerprint">Screen lock</string>
|
||||
|
||||
<!-- Biometric enrollment for devices with multiple sensors --><skip />
|
||||
<!-- Introduction title shown in biometric enrollment screen for devices that have both fingerprint and face. [CHAR LIMIT=60] -->
|
||||
<string name="multi_biometric_enroll_title">Unlock your phone securely with your face and fingerprint</string>
|
||||
<!-- Introduction subtitle shown in the biometric enrollment screen. [CHAR LIMIT=60] -->
|
||||
<string name="multi_biometric_enroll_subtitle">You can also sign in to apps and confirm payment</string>
|
||||
<!-- Section description preceding the biometric sensors available for the user to select for enrollment. [CHAR LIMIT=40] -->
|
||||
<string name="multi_biometric_enroll_setup_with">Set up unlock with:</string>
|
||||
<!-- Title for a preference containing a checkbox which if selected will allow the user to proceed to set up face unlock. [CHAR LIMIT=32] -->
|
||||
<string name="multi_biometric_enroll_face_unlock_title">Face unlock</string>
|
||||
<!-- Description for a preference containing a checkbox which if selected will allow the user to set up face unlock. [CHAR LIMIT=40] -->
|
||||
<string name="multi_biometric_enroll_face_unlock_description">Make unlocking your phone a breeze</string>
|
||||
<!-- Title for a preference containing a checkbox which if selected will allow the user to proceed to set up fingerprint unlock. [CHAR LIMIT=32] -->
|
||||
<string name="multi_biometric_enroll_fingerprint_unlock_title">Fingerprint</string>
|
||||
<!-- Description for a preference containing a checkbox which if selected will allow the user to set up fingerprint unlock. [CHAR LIMIT=40] -->
|
||||
<string name="multi_biometric_enroll_fingerprint_unlock_description">Save time unlocking phone in your pocket</string>
|
||||
<!-- Button for skipping biometric enrollment. [CHAR LIMIT=30] -->
|
||||
<string name="multi_biometric_enroll_skip">Skip</string>
|
||||
<!-- Button for starting biometric enrollment. [CHAR LIMIT=30] -->
|
||||
<string name="multi_biometric_enroll_next">Next</string>
|
||||
|
||||
<!-- Face enrollment and settings --><skip />
|
||||
<!-- Message shown in summary field when face unlock is set up. [CHAR LIMIT=40] -->
|
||||
<string name="security_settings_face_preference_summary">Face added</string>
|
||||
@@ -865,6 +885,8 @@
|
||||
<string name="security_settings_fingerprint_enroll_introduction_message_setup">Use your fingerprint to unlock your phone or approve purchases.\n\nNote: Your fingerprint may be less secure than a strong pattern or PIN.</string>
|
||||
<!-- Button text to cancel enrollment from the introduction [CHAR LIMIT=22] -->
|
||||
<string name="security_settings_fingerprint_enroll_introduction_cancel">Cancel</string>
|
||||
<!-- Button text to cancel enrollment [CHAR LIMIT=30] -->
|
||||
<string name="security_settings_fingerprint_enroll_introduction_no_thanks">No thanks</string>
|
||||
<!-- Button text to continue to the next screen from the introduction [CHAR LIMIT=22] -->
|
||||
<string name="security_settings_fingerprint_enroll_introduction_continue">Continue</string>
|
||||
<!-- Button text to cancel enrollment from the introduction (this string variant is used while in setup wizard) [CHAR LIMIT=22] -->
|
||||
@@ -939,6 +961,12 @@
|
||||
<string name="face_lock_screen_setup_skip_dialog_text" product="device">By protecting your device with a screen lock option, no one will be able to use it if it is lost or stolen. You also need a screen lock option to set up face unlock. To go back, tap Cancel. </string>
|
||||
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning that they can't continue to set face unlock. (default) [CHAR LIMIT=NONE] -->
|
||||
<string name="face_lock_screen_setup_skip_dialog_text" product="default">By protecting your phone with a screen lock option, no one will be able to use it if it is lost or stolen. You also need a screen lock option to set up face unlock. To go back, tap Cancel.</string>
|
||||
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning that they can't continue to set biometric unlock. (tablet) [CHAR LIMIT=NONE] -->
|
||||
<string name="biometrics_lock_screen_setup_skip_dialog_text" product="tablet">By protecting your tablet with a screen lock option, no one will be able to use it if it is lost or stolen. You also need a screen lock option to set up biometrics. To go back, tap Cancel. </string>
|
||||
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning that they can't continue to set biometric unlock. (device) [CHAR LIMIT=NONE] -->
|
||||
<string name="biometrics_lock_screen_setup_skip_dialog_text" product="device">By protecting your device with a screen lock option, no one will be able to use it if it is lost or stolen. You also need a screen lock option to set up biometrics. To go back, tap Cancel. </string>
|
||||
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning that they can't continue to set biometric unlock. (default) [CHAR LIMIT=NONE] -->
|
||||
<string name="biometrics_lock_screen_setup_skip_dialog_text" product="default">By protecting your phone with a screen lock option, no one will be able to use it if it is lost or stolen. You also need a screen lock option to set up biometrics. To go back, tap Cancel.</string>
|
||||
<!-- Title of dialog shown when the user tries to skip setting up a PIN, warning them of potential consequences of not doing so [CHAR LIMIT=48]-->
|
||||
<string name="lock_screen_pin_skip_title">Skip PIN setup?</string>
|
||||
<!-- Title of dialog shown when the user tries to skip setting up a password, warning them of potential consequences of not doing so [CHAR LIMIT=48]-->
|
||||
@@ -1179,11 +1207,8 @@
|
||||
<!-- Description text for screen in setup wizard asking user to set up screen lock, explaining to the user how setting up a screen lock protect them from losing data. (phone) [CHAR LIMIT=NONE] -->
|
||||
<string name="setup_lock_settings_picker_message" product="default">Prevent others from using this phone without your permission by activating device protection features. Choose the screen lock you want to use.</string>
|
||||
|
||||
<!-- Message shown in screen lock picker while setting up the backup/fallback screen lock method for fingerprint. Users can choose to use this method to unlock the screen instead of fingerprint, or when fingerprint is not accepted. [CHAR LIMIT=80] [BACKUP_MESSAGE_ID=2799884038398627882] -->
|
||||
<string name="lock_settings_picker_fingerprint_message">Choose your backup screen lock method</string>
|
||||
|
||||
<!-- Message shown in screen lock picker while setting up the backup/fallback screen lock method for face unlock. Users can choose to use this method to unlock the screen instead of face unlock, or when face unlock is not accepted. [CHAR LIMIT=80] -->
|
||||
<string name="lock_settings_picker_face_message">Choose your backup screen lock method</string>
|
||||
<!-- Message shown in screen lock picker while setting up the backup/fallback screen lock method for biometrics. Users can choose to use this method to unlock the screen instead of biometrics, or when biometrics are not accepted. [CHAR LIMIT=80] [BACKUP_MESSAGE_ID=2799884038398627882] -->
|
||||
<string name="lock_settings_picker_biometric_message">Choose your backup screen lock method</string>
|
||||
|
||||
<!-- Label for button in screen lock settings, allowing users to choose other types of screen locks. [CHAR LIMIT=40] -->
|
||||
<string name="setup_lock_settings_options_button_label">Screen lock options</string>
|
||||
@@ -1247,34 +1272,37 @@
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock pattern for fingerprint [CHAR LIMIT=45]-->
|
||||
<string name="fingerprint_unlock_set_unlock_pattern">Fingerprint + Pattern</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock PIN for fingerprint [CHAR LIMIT=45]-->
|
||||
<string name="fingerprint_unlock_set_unlock_pin">Fingerprint + PIN</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock password for fingerprint [CHAR LIMIT=45]-->
|
||||
<string name="fingerprint_unlock_set_unlock_password">Fingerprint + Password</string>
|
||||
|
||||
<!-- Title for preference that guides the user to skip fingerprint setup [CHAR LIMIT=60]-->
|
||||
<string name="fingerprint_unlock_skip_fingerprint">Continue without fingerprint</string>
|
||||
|
||||
<!-- Message shown in screen lock picker while setting up the new screen lock with fingerprint option. [CHAR LIMIT=NONE]-->
|
||||
<string name="fingerprint_unlock_title">You can unlock your phone using your fingerprint. For security, this option requires a backup screen lock.</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock pattern for face unlock [CHAR LIMIT=45]-->
|
||||
<string name="face_unlock_set_unlock_pattern">Face unlock + Pattern</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock PIN for face unlock [CHAR LIMIT=45]-->
|
||||
<string name="face_unlock_set_unlock_pin">Face unlock + PIN</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock password for face unlock [CHAR LIMIT=45]-->
|
||||
<string name="face_unlock_set_unlock_password">Face unlock + Password</string>
|
||||
|
||||
<!-- Title for preference that guides the user to skip face unlock setup [CHAR LIMIT=60]-->
|
||||
<string name="face_unlock_skip_face">Continue without face unlock</string>
|
||||
|
||||
<!-- Message shown in screen lock picker while setting up the new screen lock with face unlock option. [CHAR LIMIT=NONE] -->
|
||||
<string name="face_unlock_title">You can unlock your phone using your face. For security, this option requires a backup screen lock.</string>
|
||||
|
||||
<!-- Title for preference that guides the user through creating a backup unlock pattern for biometrics unlock [CHAR LIMIT=45]-->
|
||||
<string name="biometrics_unlock_set_unlock_pattern">Biometrics + Pattern</string>
|
||||
<!-- Title for preference that guides the user through creating a backup unlock PIN for biometrics unlock [CHAR LIMIT=45]-->
|
||||
<string name="biometrics_unlock_set_unlock_pin">Biometrics + PIN</string>
|
||||
<!-- Title for preference that guides the user through creating a backup unlock password for biometrics unlock [CHAR LIMIT=45]-->
|
||||
<string name="biometrics_unlock_set_unlock_password">Biometrics + Password</string>
|
||||
<!-- Title for preference that guides the user to skip face unlock setup [CHAR LIMIT=60]-->
|
||||
<string name="biometrics_unlock_skip_biometrics">Continue without biometrics</string>
|
||||
<!-- Message shown in screen lock picker while setting up the new screen lock with biometrics option. [CHAR LIMIT=NONE] -->
|
||||
<string name="biometrics_unlock_title">You can unlock your phone using your biometrics. For security, this option requires a backup screen lock.</string>
|
||||
|
||||
<!-- Summary for preference that has been disabled by because of the DevicePolicyAdmin, or because device encryption is enabled, or because there are credentials in the credential storage [CHAR LIMIT=50] -->
|
||||
<string name="unlock_set_unlock_disabled_summary">Disabled by admin, encryption policy, or
|
||||
credential storage</string>
|
||||
@@ -4110,6 +4138,15 @@
|
||||
<string name="lockpassword_choose_your_pattern_header_for_face">To use face unlock, set pattern</string>
|
||||
<!-- Header on first screen of choose password/PIN as backup for face unlock flow. If this string cannot be translated in under 40 characters, please translate "Set face unlock backup" [CHAR LIMIT=40] -->
|
||||
<string name="lockpassword_choose_your_pin_header_for_face">To use face unlock, set PIN</string>
|
||||
|
||||
<!-- Header on first screen of choose password/PIN as backup for biometric unlock flow. If this string cannot be translated in under 40 characters, please translate "Set biometric unlock backup" [CHAR LIMIT=40] -->
|
||||
<string name="lockpassword_choose_your_password_header_for_biometrics">To use biometrics, set password</string>
|
||||
<!-- Header on first screen of choose pattern as backup for biometric unlock flow. If this string cannot be translated in under 40 characters, please translate "Set biometric unlock backup" [CHAR LIMIT=40] -->
|
||||
<string name="lockpassword_choose_your_pattern_header_for_biometrics">To use biometrics, set pattern</string>
|
||||
<!-- Header on first screen of choose password/PIN as backup for biometric unlock flow. If this string cannot be translated in under 40 characters, please translate "Set biometric unlock backup" [CHAR LIMIT=40] -->
|
||||
<string name="lockpassword_choose_your_pin_header_for_biometrics">To use biometrics, set PIN</string>
|
||||
|
||||
|
||||
<!-- Text for button that the user should tap when they forgot their work profile password [CHAR LIMIT=40] -->
|
||||
<string name="lockpassword_forgot_password">Forgot your password?</string>
|
||||
<!-- Text for button that the user should tap when they forgot their work profile pattern [CHAR LIMIT=40] -->
|
||||
@@ -9400,6 +9437,14 @@
|
||||
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a password to encrypt the device while setting up face unlock. [CHAR LIMIT=NONE] -->
|
||||
<string name="encryption_interstitial_message_password_for_face">In addition to using your face to unlock your device, you can further protect this device by requiring your password before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require password to start your device?</string>
|
||||
|
||||
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a PIN to encrypt the device while setting up biometric unlock. [CHAR LIMIT=NONE] -->
|
||||
<string name="encryption_interstitial_message_pin_for_biometrics">In addition to using your biometrics to unlock your device, you can further protect this device by requiring your PIN before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require PIN to start your device?</string>
|
||||
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a pattern to encrypt the device while setting up biometric unlock. [CHAR LIMIT=NONE] -->
|
||||
<string name="encryption_interstitial_message_pattern_for_biometrics">In addition to using your biometrics to unlock your device, you can further protect this device by requiring your pattern before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require pattern to start your device?</string>
|
||||
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a password to encrypt the device while setting up biometric unlock. [CHAR LIMIT=NONE] -->
|
||||
<string name="encryption_interstitial_message_password_for_biometrics">In addition to using your biometrics to unlock your device, you can further protect this device by requiring your password before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require password to start your device?</string>
|
||||
|
||||
|
||||
<!-- Button label to say yes to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
|
||||
<string name="encryption_interstitial_yes">Yes</string>
|
||||
<!-- Button label to say no to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
|
||||
|
@@ -396,13 +396,7 @@
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="FingerprintHeaderStyle" parent="@*android:style/TextAppearance.DeviceDefault.Subhead">
|
||||
<item name="android:paddingTop">16dp</item>
|
||||
<item name="android:textColor">@color/primary_dark_material_light</item>
|
||||
<item name="android:lineSpacingMultiplier">1.2</item>
|
||||
</style>
|
||||
|
||||
<style name="FaceHeaderStyle" parent="@*android:style/TextAppearance.DeviceDefault.Subhead">
|
||||
<style name="BiometricHeaderStyle" parent="@*android:style/TextAppearance.DeviceDefault.Subhead">
|
||||
<item name="android:paddingTop">16dp</item>
|
||||
<item name="android:textColor">@color/primary_dark_material_light</item>
|
||||
<item name="android:lineSpacingMultiplier">1.2</item>
|
||||
|
@@ -58,6 +58,11 @@
|
||||
android:title="@string/face_unlock_skip_face"
|
||||
android:persistent="false"/>
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="unlock_skip_biometrics"
|
||||
android:title="@string/biometrics_unlock_skip_biometrics"
|
||||
android:persistent="false"/>
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:key="lock_settings_footer"
|
||||
android:selectable="false"
|
||||
|
@@ -111,6 +111,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
final boolean forFace = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
final boolean forBiometrics = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
Intent intent = getActivity().getIntent();
|
||||
mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
|
||||
mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
|
||||
@@ -121,6 +123,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_pattern_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_pattern_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_pattern_for_biometrics :
|
||||
R.string.encryption_interstitial_message_pattern;
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
@@ -129,6 +133,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_pin_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_pin_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_pin_for_biometrics :
|
||||
R.string.encryption_interstitial_message_pin;
|
||||
break;
|
||||
default:
|
||||
@@ -136,6 +142,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_password_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_password_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_password_for_biometrics :
|
||||
R.string.encryption_interstitial_message_password;
|
||||
break;
|
||||
}
|
||||
|
@@ -21,23 +21,36 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.FaceSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
|
||||
import com.android.settings.core.InstrumentedActivity;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockPattern;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Trampoline activity launched by the {@code android.settings.BIOMETRIC_ENROLL} action which
|
||||
@@ -49,18 +62,53 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
|
||||
private static final String TAG = "BiometricEnrollActivity";
|
||||
|
||||
private static final int REQUEST_CHOOSE_LOCK = 1;
|
||||
private static final int REQUEST_CONFIRM_LOCK = 2;
|
||||
|
||||
public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
|
||||
|
||||
// Intent extra. If true, biometric enrollment should skip introductory screens. Currently
|
||||
// this only applies to fingerprint.
|
||||
public static final String EXTRA_SKIP_INTRO = "skip_intro";
|
||||
|
||||
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
|
||||
private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle";
|
||||
|
||||
public static final class InternalActivity extends BiometricEnrollActivity {}
|
||||
|
||||
private int mUserId = UserHandle.myUserId();
|
||||
private boolean mConfirmingCredentials;
|
||||
@Nullable private Long mGkPwHandle;
|
||||
private BiometricEnrollCheckbox mCheckboxFace;
|
||||
private BiometricEnrollCheckbox mCheckboxFingerprint;
|
||||
@Nullable private MultiBiometricEnrollHelper mMultiBiometricEnrollHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (this instanceof InternalActivity) {
|
||||
mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mConfirmingCredentials = savedInstanceState
|
||||
.getBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, false);
|
||||
if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) {
|
||||
mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the theme in the intent so it gets propagated to other activities in the flow
|
||||
final Intent intent = getIntent();
|
||||
if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) {
|
||||
intent.putExtra(
|
||||
WizardManagerHelper.EXTRA_THEME,
|
||||
SetupWizardUtils.getThemeString(intent));
|
||||
}
|
||||
|
||||
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
|
||||
final int authenticators = getIntent().getIntExtra(
|
||||
final int authenticators = intent.getIntExtra(
|
||||
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
|
||||
|
||||
Log.d(TAG, "Authenticators: " + authenticators);
|
||||
@@ -73,9 +121,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
|
||||
if (isSetupWizard) {
|
||||
if (hasFeatureFace && hasFeatureFingerprint) {
|
||||
// TODO(b/162341940, b/152242790) this should show a multi-biometric selection
|
||||
// screen
|
||||
launchFingerprintOnlyEnroll();
|
||||
setupForMultiBiometricEnroll();
|
||||
} else if (hasFeatureFace) {
|
||||
launchFaceOnlyEnroll();
|
||||
} else if (hasFeatureFingerprint) {
|
||||
@@ -98,9 +144,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
if (authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
|
||||
launchCredentialOnlyEnroll();
|
||||
} else if (hasFeatureFace && hasFeatureFingerprint) {
|
||||
// TODO(b/162341940, b/152242790) this should show a multi-biometric selection
|
||||
// screen
|
||||
launchFingerprintOnlyEnroll();
|
||||
setupForMultiBiometricEnroll();
|
||||
} else if (hasFeatureFingerprint) {
|
||||
launchFingerprintOnlyEnroll();
|
||||
} else if (hasFeatureFace) {
|
||||
@@ -112,30 +156,216 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
|
||||
if (mGkPwHandle != null) {
|
||||
outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (mMultiBiometricEnrollHelper == null) {
|
||||
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CHOOSE_LOCK:
|
||||
mConfirmingCredentials = false;
|
||||
if (resultCode == ChooseLockPattern.RESULT_FINISHED) {
|
||||
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
|
||||
} else {
|
||||
Log.d(TAG, "Unknown result for chooseLock: " + resultCode);
|
||||
setResult(resultCode);
|
||||
finish();
|
||||
}
|
||||
break;
|
||||
case REQUEST_CONFIRM_LOCK:
|
||||
mConfirmingCredentials = false;
|
||||
if (resultCode == RESULT_OK) {
|
||||
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
|
||||
} else {
|
||||
Log.d(TAG, "Unknown result for confirmLock: " + resultCode);
|
||||
finish();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "Unknown requestCode: " + requestCode + ", finishing");
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
mMultiBiometricEnrollHelper.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
resid = SetupWizardUtils.getTheme(getIntent());
|
||||
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
if (mConfirmingCredentials || mMultiBiometricEnrollHelper != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isChangingConfigurations()) {
|
||||
Log.d(TAG, "Finishing in onStop");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupForMultiBiometricEnroll() {
|
||||
setContentView(R.layout.biometric_enroll_layout);
|
||||
|
||||
mCheckboxFace = findViewById(R.id.checkbox_face);
|
||||
mCheckboxFingerprint = findViewById(R.id.checkbox_fingerprint);
|
||||
|
||||
mCheckboxFace.setListener(this::updateNextButton);
|
||||
mCheckboxFingerprint.setListener(this::updateNextButton);
|
||||
|
||||
final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
|
||||
final FaceManager faceManager = getSystemService(FaceManager.class);
|
||||
final List<FingerprintSensorProperties> fpProperties =
|
||||
fingerprintManager.getSensorProperties();
|
||||
final List<FaceSensorProperties> faceProperties = faceManager.getSensorProperties();
|
||||
|
||||
// This would need to be updated for devices with multiple sensors of the same modality
|
||||
final boolean maxFacesEnrolled = faceManager.getEnrolledFaces(mUserId).size()
|
||||
>= faceProperties.get(0).maxTemplatesAllowed;
|
||||
final boolean maxFingerprintsEnrolled = fingerprintManager.getEnrolledFingerprints(mUserId)
|
||||
.size() >= fpProperties.get(0).maxTemplatesAllowed;
|
||||
|
||||
if (maxFacesEnrolled) {
|
||||
mCheckboxFace.setEnabled(false);
|
||||
mCheckboxFace.setDescription(R.string.face_intro_error_max);
|
||||
}
|
||||
|
||||
if (maxFingerprintsEnrolled) {
|
||||
mCheckboxFingerprint.setEnabled(false);
|
||||
mCheckboxFingerprint.setDescription(R.string.fingerprint_intro_error_max);
|
||||
}
|
||||
|
||||
final FooterBarMixin footerBarMixin = ((GlifLayout) findViewById(R.id.setup_wizard_layout))
|
||||
.getMixin(FooterBarMixin.class);
|
||||
footerBarMixin.setSecondaryButton(new FooterButton.Builder(this)
|
||||
.setText(R.string.multi_biometric_enroll_skip)
|
||||
.setListener(this::onButtonNegative)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build());
|
||||
|
||||
footerBarMixin.setPrimaryButton(new FooterButton.Builder(this)
|
||||
.setText(R.string.multi_biometric_enroll_next)
|
||||
.setListener(this::onButtonPositive)
|
||||
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build());
|
||||
|
||||
footerBarMixin.getSecondaryButton().setVisibility(View.VISIBLE);
|
||||
footerBarMixin.getPrimaryButton().setVisibility(View.VISIBLE);
|
||||
|
||||
if (!mConfirmingCredentials && mGkPwHandle == null) {
|
||||
mConfirmingCredentials = true;
|
||||
if (!userHasPassword(mUserId)) {
|
||||
launchChooseLock();
|
||||
} else {
|
||||
launchConfirmLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNextButton(View view) {
|
||||
final boolean canEnrollAny = canEnrollFace() || canEnrollFingerprint();
|
||||
|
||||
final FooterBarMixin footerBarMixin = ((GlifLayout) findViewById(R.id.setup_wizard_layout))
|
||||
.getMixin(FooterBarMixin.class);
|
||||
footerBarMixin.getPrimaryButton().setEnabled(canEnrollAny);
|
||||
}
|
||||
|
||||
private void onButtonPositive(View view) {
|
||||
// Start the state machine according to checkboxes, taking max enrolled into account
|
||||
mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId,
|
||||
canEnrollFace(), canEnrollFingerprint(), mGkPwHandle);
|
||||
mMultiBiometricEnrollHelper.startNextStep();
|
||||
}
|
||||
|
||||
private void onButtonNegative(View view) {
|
||||
setResult(RESULT_SKIP);
|
||||
finish();
|
||||
}
|
||||
|
||||
private boolean canEnrollFace() {
|
||||
return mCheckboxFace.isEnabled() && mCheckboxFace.isChecked();
|
||||
}
|
||||
|
||||
private boolean canEnrollFingerprint() {
|
||||
return mCheckboxFingerprint.isEnabled() && mCheckboxFingerprint.isChecked();
|
||||
}
|
||||
|
||||
private boolean userHasPassword(int userId) {
|
||||
final UserManager userManager = getSystemService(UserManager.class);
|
||||
final int passwordQuality = new LockPatternUtils(this)
|
||||
.getActivePasswordQuality(userManager.getCredentialOwnerProfile(userId));
|
||||
return passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
}
|
||||
|
||||
private void launchChooseLock() {
|
||||
Log.d(TAG, "launchChooseLock");
|
||||
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
|
||||
|
||||
if (mUserId != UserHandle.USER_NULL) {
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
}
|
||||
startActivityForResult(intent, REQUEST_CHOOSE_LOCK);
|
||||
}
|
||||
|
||||
private void launchConfirmLock() {
|
||||
Log.d(TAG, "launchConfirmLock");
|
||||
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
|
||||
builder.setRequestCode(REQUEST_CONFIRM_LOCK)
|
||||
.setRequestGatekeeperPasswordHandle(true)
|
||||
.setForegroundOnly(true)
|
||||
.setReturnCredentials(true);
|
||||
if (mUserId != UserHandle.USER_NULL) {
|
||||
builder.setUserId(mUserId);
|
||||
}
|
||||
final boolean launched = builder.show();
|
||||
if (!launched) {
|
||||
// This shouldn't happen, as we should only end up at this step if a lock thingy is
|
||||
// already set.
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should only be used to launch enrollment for single-sensor devices, which use
|
||||
* FLAG_ACTIVITY_FORWARD_RESULT path.
|
||||
*
|
||||
* @param intent Enrollment activity that should be started (e.g. FaceEnrollIntroduction.class,
|
||||
* etc).
|
||||
*/
|
||||
private void launchEnrollActivity(@NonNull Intent intent) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
|
||||
byte[] hardwareAuthToken = null;
|
||||
if (this instanceof InternalActivity) {
|
||||
// Propagate challenge and user Id from ChooseLockGeneric.
|
||||
final byte[] token = getIntent()
|
||||
.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||
final int userId = getIntent()
|
||||
.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
|
||||
final long gkPwHandle = getIntent().getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
if (gkPwHandle != 0L) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
hardwareAuthToken = getIntent().getByteArrayExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||
}
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
BiometricUtils.launchEnrollForResult(this, intent, 0 /* requestCode */, hardwareAuthToken,
|
||||
null /* gkPwHandle */, mUserId);
|
||||
}
|
||||
|
||||
private void launchCredentialOnlyEnroll() {
|
||||
@@ -152,40 +382,18 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
// ChooseLockGeneric can request to start fingerprint enroll bypassing the intro screen.
|
||||
if (getIntent().getBooleanExtra(EXTRA_SKIP_INTRO, false)
|
||||
&& this instanceof InternalActivity) {
|
||||
intent = getFingerprintFindSensorIntent();
|
||||
intent = BiometricUtils.getFingerprintFindSensorIntent(this, getIntent());
|
||||
} else {
|
||||
intent = getFingerprintIntroIntent();
|
||||
intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent());
|
||||
}
|
||||
launchEnrollActivity(intent);
|
||||
}
|
||||
|
||||
private void launchFaceOnlyEnroll() {
|
||||
final Intent intent = getFaceIntroIntent();
|
||||
final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent());
|
||||
launchEnrollActivity(intent);
|
||||
}
|
||||
|
||||
private Intent getFingerprintFindSensorIntent() {
|
||||
Intent intent = new Intent(this, FingerprintEnrollFindSensor.class);
|
||||
SetupWizardUtils.copySetupExtras(getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
private Intent getFingerprintIntroIntent() {
|
||||
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
|
||||
Intent intent = new Intent(this, SetupFingerprintEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(this, FingerprintEnrollIntroduction.class);
|
||||
}
|
||||
}
|
||||
|
||||
private Intent getFaceIntroIntent() {
|
||||
Intent intent = new Intent(this, FaceEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.BIOMETRIC_ENROLL_ACTIVITY;
|
||||
|
@@ -57,6 +57,9 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
* starting the next activity. However, this leads to broken 'Back'
|
||||
* behavior. So, now an activity does not finish itself until it gets this
|
||||
* result.
|
||||
*
|
||||
* This must be the same as
|
||||
* {@link com.android.settings.password.ChooseLockPattern#RESULT_FINISHED}
|
||||
*/
|
||||
public static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
|
||||
@@ -78,6 +81,11 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
public static final int LEARN_MORE_REQUEST = 3;
|
||||
public static final int CONFIRM_REQUEST = 4;
|
||||
public static final int ENROLL_REQUEST = 5;
|
||||
/**
|
||||
* Request code when starting another biometric enrollment from within a biometric flow. For
|
||||
* example, when starting fingerprint enroll after face enroll.
|
||||
*/
|
||||
public static final int ENROLL_NEXT_BIOMETRIC_REQUEST = 6;
|
||||
|
||||
protected boolean mLaunchedConfirmLock;
|
||||
protected byte[] mToken;
|
||||
|
115
src/com/android/settings/biometrics/BiometricEnrollCheckbox.java
Normal file
115
src/com/android/settings/biometrics/BiometricEnrollCheckbox.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.settings.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Widget contain space for an icon, title, description, and checkbox. On devices with multiple
|
||||
* biometric sensors, allows users to choose sensors during {@link BiometricEnrollActivity}.
|
||||
*/
|
||||
public class BiometricEnrollCheckbox extends LinearLayout {
|
||||
|
||||
@NonNull private final CheckBox mCheckBox;
|
||||
@NonNull private final TextView mDescriptionView;
|
||||
@Nullable private View.OnClickListener mListener;
|
||||
|
||||
public BiometricEnrollCheckbox(Context context) {
|
||||
this(context, null /* attrs */);
|
||||
}
|
||||
|
||||
public BiometricEnrollCheckbox(Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0 /* defStyleAttr */);
|
||||
}
|
||||
|
||||
public BiometricEnrollCheckbox(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
|
||||
}
|
||||
|
||||
public BiometricEnrollCheckbox(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.biometric_enroll_checkbox,
|
||||
this, true /* attachToRoot */);
|
||||
|
||||
mCheckBox = findViewById(R.id.checkbox);
|
||||
final ImageView iconView = findViewById(R.id.icon);
|
||||
final TextView titleView = findViewById(R.id.title);
|
||||
mDescriptionView = findViewById(R.id.description);
|
||||
|
||||
setOnClickListener(view -> {
|
||||
if (isEnabled()) {
|
||||
mCheckBox.toggle();
|
||||
}
|
||||
if (mListener != null) {
|
||||
mListener.onClick(view);
|
||||
}
|
||||
});
|
||||
|
||||
final TypedArray a = context
|
||||
.obtainStyledAttributes(attrs, R.styleable.BiometricEnrollCheckbox);
|
||||
try {
|
||||
final Drawable icon =
|
||||
a.getDrawable(R.styleable.BiometricEnrollCheckbox_icon);
|
||||
final CharSequence title =
|
||||
a.getText(R.styleable.BiometricEnrollCheckbox_title);
|
||||
final CharSequence description =
|
||||
a.getText(R.styleable.BiometricEnrollCheckbox_description);
|
||||
iconView.setImageDrawable(icon);
|
||||
titleView.setText(title);
|
||||
mDescriptionView.setText(description);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(View.OnClickListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
mCheckBox.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return mCheckBox.isChecked();
|
||||
}
|
||||
|
||||
public void setDescription(@StringRes int resId) {
|
||||
mDescriptionView.setText(resId);
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -29,9 +29,7 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
@@ -43,6 +41,8 @@ import com.google.android.setupdesign.span.LinkSpan;
|
||||
public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
implements LinkSpan.OnClickListener {
|
||||
|
||||
private static final String TAG = "BiometricEnrollIntroduction";
|
||||
|
||||
private static final String KEY_CONFIRMING_CREDENTIALS = "confirming_credentials";
|
||||
|
||||
private UserManager mUserManager;
|
||||
@@ -164,7 +164,8 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
// No password registered, launch into enrollment wizard.
|
||||
mConfirmingCredentials = true;
|
||||
launchChooseLock();
|
||||
} else if (!BiometricUtils.containsGatekeeperPassword(getIntent()) && mToken == null) {
|
||||
} else if (!BiometricUtils.containsGatekeeperPasswordHandle(getIntent())
|
||||
&& mToken == null) {
|
||||
// It's possible to have a token but mLaunchedConfirmLock == false, since
|
||||
// ChooseLockGeneric can pass us a token.
|
||||
mConfirmingCredentials = true;
|
||||
@@ -220,7 +221,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
}
|
||||
|
||||
private void launchChooseLock() {
|
||||
Intent intent = getChooseLockIntent();
|
||||
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
|
||||
@@ -240,27 +241,11 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
if (mUserId != UserHandle.USER_NULL) {
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
}
|
||||
BiometricUtils.copyMultiBiometricExtras(getIntent(), intent);
|
||||
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
}
|
||||
|
||||
protected Intent getChooseLockIntent() {
|
||||
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
|
||||
// Default to PIN lock in setup wizard
|
||||
Intent intent = new Intent(this, SetupChooseLockGeneric.class);
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
intent.putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
|
||||
}
|
||||
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(this, ChooseLockGeneric.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
|
||||
@@ -301,6 +286,12 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
}
|
||||
} else if (requestCode == LEARN_MORE_REQUEST) {
|
||||
overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out);
|
||||
} else if (requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST) {
|
||||
Log.d(TAG, "ENROLL_NEXT_BIOMETRIC_REQUEST, result: " + resultCode);
|
||||
if (resultCode != RESULT_CANCELED) {
|
||||
setResult(resultCode, data);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
@@ -16,16 +16,35 @@
|
||||
|
||||
package com.android.settings.biometrics;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
/**
|
||||
* Common biometric utilities.
|
||||
*/
|
||||
public class BiometricUtils {
|
||||
private static final String TAG = "BiometricUtils";
|
||||
/**
|
||||
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
|
||||
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
|
||||
@@ -36,24 +55,29 @@ public class BiometricUtils {
|
||||
* @param challenge Unique biometric challenge from FingerprintManager/FaceManager
|
||||
* @return
|
||||
*/
|
||||
public static byte[] requestGatekeeperHat(Context context, Intent result, int userId,
|
||||
long challenge) {
|
||||
final long gatekeeperPasswordHandle = result.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
if (gatekeeperPasswordHandle == 0L) {
|
||||
public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
|
||||
int userId, long challenge) {
|
||||
if (!containsGatekeeperPasswordHandle(result)) {
|
||||
throw new IllegalStateException("Gatekeeper Password is missing!!");
|
||||
}
|
||||
final long gatekeeperPasswordHandle = result.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
return requestGatekeeperHat(context, gatekeeperPasswordHandle, userId, challenge);
|
||||
}
|
||||
|
||||
public static byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
|
||||
long challenge) {
|
||||
final LockPatternUtils utils = new LockPatternUtils(context);
|
||||
return utils.verifyGatekeeperPasswordHandle(gatekeeperPasswordHandle, challenge, userId)
|
||||
return utils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId)
|
||||
.getGatekeeperHAT();
|
||||
}
|
||||
|
||||
public static boolean containsGatekeeperPassword(Intent data) {
|
||||
if (data == null) {
|
||||
return false;
|
||||
public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
|
||||
return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
|
||||
}
|
||||
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L) != 0L;
|
||||
|
||||
public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
|
||||
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,16 +88,157 @@ public class BiometricUtils {
|
||||
* @param context Caller's context
|
||||
* @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
|
||||
*/
|
||||
public static void removeGatekeeperPasswordHandle(Context context, Intent data) {
|
||||
public static void removeGatekeeperPasswordHandle(@NonNull Context context,
|
||||
@Nullable Intent data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
final long gatekeeperPasswordsHandle = data.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
if (gatekeeperPasswordsHandle == 0L) {
|
||||
if (!containsGatekeeperPasswordHandle(data)) {
|
||||
return;
|
||||
}
|
||||
removeGatekeeperPasswordHandle(context, getGatekeeperPasswordHandle(data));
|
||||
}
|
||||
|
||||
public static void removeGatekeeperPasswordHandle(@NonNull Context context, long handle) {
|
||||
final LockPatternUtils utils = new LockPatternUtils(context);
|
||||
utils.removeGatekeeperPasswordHandle(gatekeeperPasswordsHandle);
|
||||
utils.removeGatekeeperPasswordHandle(handle);
|
||||
Log.d(TAG, "Removed handle");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting ChooseLock*
|
||||
*/
|
||||
public static Intent getChooseLockIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
|
||||
// Default to PIN lock in setup wizard
|
||||
Intent intent = new Intent(context, SetupChooseLockGeneric.class);
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
intent.putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment
|
||||
.EXTRA_SHOW_OPTIONS_BUTTON, true);
|
||||
}
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(context, ChooseLockGeneric.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FingerprintEnrollFindSensor
|
||||
*/
|
||||
public static Intent getFingerprintFindSensorIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
Intent intent = new Intent(context, FingerprintEnrollFindSensor.class);
|
||||
SetupWizardUtils.copySetupExtras(activityIntent, intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FingerprintEnrollIntroduction
|
||||
*/
|
||||
public static Intent getFingerprintIntroIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
|
||||
Intent intent = new Intent(context, SetupFingerprintEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(context, FingerprintEnrollIntroduction.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FaceEnrollIntroduction
|
||||
*/
|
||||
public static Intent getFaceIntroIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
Intent intent = new Intent(context, FaceEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activity Reference to the calling activity, used to startActivity
|
||||
* @param intent Intent pointing to the enrollment activity
|
||||
* @param requestCode If non-zero, will invoke startActivityForResult instead of startActivity
|
||||
* @param hardwareAuthToken HardwareAuthToken from Gatekeeper
|
||||
* @param userId User to request enrollment for
|
||||
*/
|
||||
public static void launchEnrollForResult(@NonNull BiometricEnrollActivity activity,
|
||||
@NonNull Intent intent, int requestCode,
|
||||
@Nullable byte[] hardwareAuthToken, @Nullable Long gkPwHandle, int userId) {
|
||||
if (hardwareAuthToken != null) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
||||
hardwareAuthToken);
|
||||
}
|
||||
if (gkPwHandle != null) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
}
|
||||
|
||||
if (activity instanceof BiometricEnrollActivity.InternalActivity) {
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
}
|
||||
|
||||
if (requestCode != 0) {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
} else {
|
||||
activity.startActivity(intent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activity Activity that we want to check
|
||||
* @return True if the activity is going through a multi-biometric enrollment flow.
|
||||
*/
|
||||
public static boolean isMultiBiometricEnrollmentFlow(@NonNull Activity activity) {
|
||||
return activity.getIntent().hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
|
||||
}
|
||||
|
||||
public static void copyMultiBiometricExtras(@NonNull Intent fromIntent,
|
||||
@NonNull Intent toIntent) {
|
||||
final PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
|
||||
MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, null);
|
||||
if (pendingIntent != null) {
|
||||
toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current biometric enrollment (e.g. face) should be followed by another one (e.g.
|
||||
* fingerprint) (see {@link #isMultiBiometricEnrollmentFlow(Activity)}), retrieves the
|
||||
* PendingIntent pointing to the next enrollment and starts it. The caller will receive the
|
||||
* result in onActivityResult.
|
||||
* @return true if the next enrollment was started
|
||||
*/
|
||||
public static boolean tryStartingNextBiometricEnroll(@NonNull Activity activity,
|
||||
int requestCode) {
|
||||
final PendingIntent pendingIntent = (PendingIntent) activity.getIntent()
|
||||
.getExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
|
||||
if (pendingIntent != null) {
|
||||
try {
|
||||
Log.d(TAG, "Starting pendingIntent: " + pendingIntent);
|
||||
IntentSender intentSender = pendingIntent.getIntentSender();
|
||||
activity.startIntentSenderForResult(intentSender, requestCode,
|
||||
null /* fillInIntent */, 0 /* flagMask */, 0 /* flagValues */,
|
||||
0 /* extraFlags */);
|
||||
return true;
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.e(TAG, "Pending intent canceled: " + e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.settings.biometrics;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
/**
|
||||
* Helper for {@link BiometricEnrollActivity} when multiple sensors exist on a device.
|
||||
*/
|
||||
public class MultiBiometricEnrollHelper {
|
||||
|
||||
private static final String TAG = "MultiBiometricEnrollHelper";
|
||||
|
||||
private static final int REQUEST_FACE_ENROLL = 3000;
|
||||
private static final int REQUEST_FINGERPRINT_ENROLL = 3001;
|
||||
|
||||
public static final String EXTRA_ENROLL_AFTER_FACE = "enroll_after_face";
|
||||
|
||||
@NonNull private final BiometricEnrollActivity mActivity;
|
||||
private final long mGkPwHandle;
|
||||
private final int mUserId;
|
||||
private final boolean mRequestEnrollFace;
|
||||
private final boolean mRequestEnrollFingerprint;
|
||||
|
||||
MultiBiometricEnrollHelper(@NonNull BiometricEnrollActivity activity, int userId,
|
||||
boolean enrollFace, boolean enrollFingerprint, long gkPwHandle) {
|
||||
mActivity = activity;
|
||||
mUserId = userId;
|
||||
mGkPwHandle = gkPwHandle;
|
||||
mRequestEnrollFace = enrollFace;
|
||||
mRequestEnrollFingerprint = enrollFingerprint;
|
||||
}
|
||||
|
||||
void startNextStep() {
|
||||
if (mRequestEnrollFace) {
|
||||
launchFaceEnroll();
|
||||
} else if (mRequestEnrollFingerprint) {
|
||||
launchFingerprintEnroll();
|
||||
} else {
|
||||
mActivity.setResult(BiometricEnrollIntroduction.RESULT_SKIP);
|
||||
mActivity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void launchFaceEnroll() {
|
||||
final FaceManager faceManager = mActivity.getSystemService(FaceManager.class);
|
||||
faceManager.generateChallenge((sensorId, challenge) -> {
|
||||
final byte[] hardwareAuthToken = BiometricUtils.requestGatekeeperHat(mActivity,
|
||||
mGkPwHandle, mUserId, challenge);
|
||||
final Intent faceIntent = BiometricUtils.getFaceIntroIntent(mActivity,
|
||||
mActivity.getIntent());
|
||||
|
||||
if (mRequestEnrollFingerprint) {
|
||||
// Give FaceEnroll a pendingIntent pointing to fingerprint enrollment, so that it
|
||||
// can be started when user skips or finishes face enrollment.
|
||||
final Intent fpIntent = BiometricUtils.getFingerprintIntroIntent(mActivity,
|
||||
mActivity.getIntent());
|
||||
fpIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
|
||||
final PendingIntent fpAfterFaceIntent = PendingIntent.getActivity(mActivity,
|
||||
0 /* requestCode */, fpIntent, 0 /* flags */);
|
||||
faceIntent.putExtra(EXTRA_ENROLL_AFTER_FACE, fpAfterFaceIntent);
|
||||
}
|
||||
|
||||
BiometricUtils.launchEnrollForResult(mActivity, faceIntent, REQUEST_FACE_ENROLL,
|
||||
hardwareAuthToken, mGkPwHandle, mUserId);
|
||||
});
|
||||
}
|
||||
|
||||
private void launchFingerprintEnroll() {
|
||||
final FingerprintManager fingerprintManager = mActivity
|
||||
.getSystemService(FingerprintManager.class);
|
||||
fingerprintManager.generateChallenge(((sensorId, challenge) -> {
|
||||
final byte[] hardwareAuthToken = BiometricUtils.requestGatekeeperHat(mActivity,
|
||||
mGkPwHandle, mUserId, challenge);
|
||||
final Intent intent = BiometricUtils.getFingerprintIntroIntent(mActivity,
|
||||
mActivity.getIntent());
|
||||
BiometricUtils.launchEnrollForResult(mActivity, intent, REQUEST_FINGERPRINT_ENROLL,
|
||||
hardwareAuthToken, mGkPwHandle, mUserId);
|
||||
}));
|
||||
}
|
||||
|
||||
void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Log.d(TAG, "RequestCode: " + requestCode + " resultCode: " + resultCode);
|
||||
if (resultCode != Activity.RESULT_CANCELED) {
|
||||
BiometricUtils.removeGatekeeperPasswordHandle(mActivity, mGkPwHandle);
|
||||
mActivity.setResult(resultCode);
|
||||
mActivity.finish();
|
||||
}
|
||||
}
|
||||
}
|
@@ -33,6 +33,7 @@ import android.widget.TextView;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
@@ -183,6 +184,7 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
}
|
||||
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
|
||||
BiometricUtils.copyMultiBiometricExtras(getIntent(), intent);
|
||||
final String flattenedString = getString(R.string.config_face_enroll);
|
||||
if (!TextUtils.isEmpty(flattenedString)) {
|
||||
ComponentName componentName = ComponentName.unflattenFromString(flattenedString);
|
||||
|
@@ -16,17 +16,23 @@
|
||||
|
||||
package com.android.settings.biometrics.face;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.face.FaceSensorProperties;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollIntroduction;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.biometrics.MultiBiometricEnrollHelper;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
@@ -37,6 +43,8 @@ import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
import com.google.android.setupdesign.template.RequireScrollMixin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
private static final String TAG = "FaceEnrollIntroduction";
|
||||
@@ -44,6 +52,20 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
private FaceManager mFaceManager;
|
||||
private FaceFeatureProvider mFaceFeatureProvider;
|
||||
|
||||
@Override
|
||||
protected void onCancelButtonClick(View view) {
|
||||
if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST)) {
|
||||
super.onCancelButtonClick(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSkipButtonClick(View view) {
|
||||
if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST)) {
|
||||
super.onSkipButtonClick(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -53,7 +75,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
.getFaceFeatureProvider();
|
||||
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_no_thanks)
|
||||
@@ -62,16 +83,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
} else {
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_no_thanks)
|
||||
.setListener(this::onCancelButtonClick)
|
||||
.setButtonType(FooterButton.ButtonType.CANCEL)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
FooterButton.Builder nextButtonBuilder = new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_agree)
|
||||
@@ -99,13 +110,15 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
// This path is an entry point for SetNewPasswordController, e.g.
|
||||
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
|
||||
if (mToken == null && BiometricUtils.containsGatekeeperPassword(getIntent())) {
|
||||
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
||||
mFooterBarMixin.getPrimaryButton().setEnabled(false);
|
||||
// We either block on generateChallenge, or need to gray out the "next" button until
|
||||
// the challenge is ready. Let's just do this for now.
|
||||
mFaceManager.generateChallenge((sensorId, challenge) -> {
|
||||
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
|
||||
if (BiometricUtils.isMultiBiometricEnrollmentFlow(this)) {
|
||||
BiometricUtils.removeGatekeeperPasswordHandle(this, getIntent());
|
||||
}
|
||||
mFooterBarMixin.getPrimaryButton().setEnabled(true);
|
||||
});
|
||||
}
|
||||
@@ -160,8 +173,9 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
private boolean maxFacesEnrolled() {
|
||||
if (mFaceManager != null) {
|
||||
final int max = getResources().getInteger(
|
||||
com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
|
||||
final List<FaceSensorProperties> props = mFaceManager.getSensorProperties();
|
||||
// This will need to be updated for devices with multiple face sensors.
|
||||
final int max = props.get(0).maxTemplatesAllowed;
|
||||
final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size();
|
||||
return numEnrolledFaces >= max;
|
||||
} else {
|
||||
|
@@ -236,7 +236,7 @@ public class FaceSettings extends DashboardFragment {
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (mToken == null && !BiometricUtils.containsGatekeeperPassword(data)) {
|
||||
if (mToken == null && !BiometricUtils.containsGatekeeperPasswordHandle(data)) {
|
||||
Log.e(TAG, "No credential");
|
||||
finish();
|
||||
}
|
||||
|
@@ -63,7 +63,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
|
||||
|
||||
// This is an entry point for SetNewPasswordController, e.g.
|
||||
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
|
||||
if (mToken == null && BiometricUtils.containsGatekeeperPassword(getIntent())) {
|
||||
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
||||
final FingerprintManager fpm = getSystemService(FingerprintManager.class);
|
||||
fpm.generateChallenge((sensorId, challenge) -> {
|
||||
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
|
||||
@@ -79,6 +79,9 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
|
||||
} else if (mToken != null) {
|
||||
// HAT passed in from somewhere else, such as FingerprintEnrollIntroduction
|
||||
startLookingForFingerprint();
|
||||
} else {
|
||||
// There's something wrong with the enrollment flow, this should never happen.
|
||||
throw new IllegalStateException("HAT and GkPwHandle both missing...");
|
||||
}
|
||||
|
||||
View animationView = findViewById(R.id.fingerprint_sensor_location_animation);
|
||||
|
@@ -21,6 +21,7 @@ import android.app.settings.SettingsEnums;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
@@ -28,6 +29,7 @@ import android.widget.TextView;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollIntroduction;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.HelpUtils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
@@ -36,6 +38,8 @@ import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
private static final String TAG = "FingerprintIntro";
|
||||
@@ -45,13 +49,14 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
|
||||
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_cancel)
|
||||
.setListener(this::onCancelButtonClick)
|
||||
.setText(getNegativeButtonTextId())
|
||||
.setListener(this::onSkipButtonClick)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
@@ -67,6 +72,10 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
);
|
||||
}
|
||||
|
||||
int getNegativeButtonTextId() {
|
||||
return R.string.security_settings_fingerprint_enroll_introduction_no_thanks;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDisabledByAdmin() {
|
||||
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
|
||||
@@ -117,8 +126,10 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@Override
|
||||
protected int checkMaxEnrolled() {
|
||||
if (mFingerprintManager != null) {
|
||||
final int max = getResources().getInteger(
|
||||
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
|
||||
final List<FingerprintSensorProperties> props =
|
||||
mFingerprintManager.getSensorProperties();
|
||||
// This will need to be updated for devices with multiple fingerprint sensors
|
||||
final int max = props.get(0).maxTemplatesAllowed;
|
||||
final int numEnrolledFingerprints =
|
||||
mFingerprintManager.getEnrolledFingerprints(mUserId).size();
|
||||
if (numEnrolledFingerprints >= max) {
|
||||
@@ -147,7 +158,12 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected Intent getEnrollingIntent() {
|
||||
return new Intent(this, FingerprintEnrollFindSensor.class);
|
||||
final Intent intent = new Intent(this, FingerprintEnrollFindSensor.class);
|
||||
if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
|
||||
BiometricUtils.getGatekeeperPasswordHandle(getIntent()));
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -570,7 +570,7 @@ public class FingerprintSettings extends SubSettings {
|
||||
if (requestCode == CONFIRM_REQUEST || requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
|
||||
mLaunchedConfirm = false;
|
||||
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
|
||||
if (data != null && BiometricUtils.containsGatekeeperPassword(data)) {
|
||||
if (data != null && BiometricUtils.containsGatekeeperPasswordHandle(data)) {
|
||||
mFingerprintManager.generateChallenge((sensorId, challenge) -> {
|
||||
mToken = BiometricUtils.requestGatekeeperHat(getActivity(), data,
|
||||
mUserId, challenge);
|
||||
|
@@ -32,7 +32,9 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
|
||||
@@ -58,29 +60,24 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getNegativeButtonTextId() {
|
||||
return R.string.security_settings_face_enroll_introduction_cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(KEY_LOCK_SCREEN_PRESENT, mAlreadyHadLockScreenSetup);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getChooseLockIntent() {
|
||||
Intent intent = new Intent(this, SetupChooseLockGeneric.class);
|
||||
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
intent.putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
|
||||
}
|
||||
SetupWizardUtils.copySetupExtras(getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getEnrollingIntent() {
|
||||
final Intent intent = new Intent(this, SetupFingerprintEnrollFindSensor.class);
|
||||
if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
|
||||
BiometricUtils.getGatekeeperPasswordHandle(getIntent()));
|
||||
}
|
||||
SetupWizardUtils.copySetupExtras(getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
@@ -157,6 +154,11 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSkipButtonClick(View view) {
|
||||
onCancelButtonClick(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate lock screen metrics if the user goes back from the fingerprint setup screen
|
||||
* after having added lock screen to his device.
|
||||
|
@@ -103,6 +103,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
private static final String TAG = "ChooseLockGenericFragment";
|
||||
private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint";
|
||||
private static final String KEY_SKIP_FACE = "unlock_skip_face";
|
||||
private static final String KEY_SKIP_BIOMETRICS = "unlock_skip_biometrics";
|
||||
private static final String PASSWORD_CONFIRMED = "password_confirmed";
|
||||
private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
|
||||
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
|
||||
@@ -175,6 +176,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
|
||||
protected boolean mForFingerprint = false;
|
||||
protected boolean mForFace = false;
|
||||
protected boolean mForBiometrics = false;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -216,6 +218,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mForBiometrics = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
|
||||
mRequestedMinComplexity = intent
|
||||
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mCallerAppName =
|
||||
@@ -303,17 +308,20 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
}
|
||||
|
||||
protected void addHeaderView() {
|
||||
setHeaderView(R.layout.choose_lock_generic_biometric_header);
|
||||
TextView textView = getHeaderView().findViewById(R.id.biometric_header_description);
|
||||
|
||||
if (mForFingerprint) {
|
||||
setHeaderView(R.layout.choose_lock_generic_fingerprint_header);
|
||||
if (mIsSetNewPassword) {
|
||||
((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description))
|
||||
.setText(R.string.fingerprint_unlock_title);
|
||||
textView.setText(R.string.fingerprint_unlock_title);
|
||||
}
|
||||
} else if (mForFace) {
|
||||
setHeaderView(R.layout.choose_lock_generic_face_header);
|
||||
if (mIsSetNewPassword) {
|
||||
((TextView) getHeaderView().findViewById(R.id.face_header_description))
|
||||
.setText(R.string.face_unlock_title);
|
||||
textView.setText(R.string.face_unlock_title);
|
||||
}
|
||||
} else if (mForBiometrics) {
|
||||
if (mIsSetNewPassword) {
|
||||
textView.setText(R.string.biometrics_unlock_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +336,8 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
// unlock method to an insecure one
|
||||
showFactoryResetProtectionWarningDialog(key);
|
||||
return true;
|
||||
} else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)) {
|
||||
} else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)
|
||||
|| KEY_SKIP_BIOMETRICS.equals(key)) {
|
||||
Intent chooseLockGenericIntent = new Intent(getActivity(),
|
||||
getInternalActivityClass());
|
||||
chooseLockGenericIntent.setAction(getIntent().getAction());
|
||||
@@ -381,8 +390,8 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
unlockMethodIntent);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
|
||||
mForFingerprint);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE,
|
||||
mForFace);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, mForFace);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, mForBiometrics);
|
||||
// If the caller requested Gatekeeper Password to be returned, we assume it came
|
||||
// from biometric enrollment. This should be cleaned up, since requesting
|
||||
// Gatekeeper Password should not imply it came from biometric setup/settings.
|
||||
@@ -531,6 +540,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
|
||||
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
|
||||
findPreference(KEY_SKIP_FACE).setViewId(R.id.lock_none);
|
||||
findPreference(KEY_SKIP_BIOMETRICS).setViewId(R.id.lock_none);
|
||||
findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
|
||||
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
|
||||
}
|
||||
@@ -569,6 +579,12 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
setPreferenceTitle(ScreenLockType.PIN, R.string.face_unlock_set_unlock_pin);
|
||||
setPreferenceTitle(ScreenLockType.PASSWORD,
|
||||
R.string.face_unlock_set_unlock_password);
|
||||
} else if (mForBiometrics) {
|
||||
setPreferenceTitle(ScreenLockType.PATTERN,
|
||||
R.string.biometrics_unlock_set_unlock_pattern);
|
||||
setPreferenceTitle(ScreenLockType.PIN, R.string.biometrics_unlock_set_unlock_pin);
|
||||
setPreferenceTitle(ScreenLockType.PASSWORD,
|
||||
R.string.biometrics_unlock_set_unlock_password);
|
||||
}
|
||||
|
||||
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
|
||||
@@ -584,6 +600,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
if (!(mForFace && mIsSetNewPassword)) {
|
||||
removePreference(KEY_SKIP_FACE);
|
||||
}
|
||||
if (!(mForBiometrics && mIsSetNewPassword)) {
|
||||
removePreference(KEY_SKIP_BIOMETRICS);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
|
||||
@@ -727,6 +746,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
.setRequestedMinComplexity(mRequestedMinComplexity)
|
||||
.setForFingerprint(mForFingerprint)
|
||||
.setForFace(mForFace)
|
||||
.setForBiometrics(mForBiometrics)
|
||||
.setUserId(mUserId)
|
||||
.setRequestGatekeeperPasswordHandle(mRequestGatekeeperPasswordHandle);
|
||||
if (mUserPassword != null) {
|
||||
@@ -743,6 +763,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
new ChooseLockPattern.IntentBuilder(getContext())
|
||||
.setForFingerprint(mForFingerprint)
|
||||
.setForFace(mForFace)
|
||||
.setForBiometrics(mForBiometrics)
|
||||
.setUserId(mUserId)
|
||||
.setRequestGatekeeperPasswordHandle(mRequestGatekeeperPasswordHandle);
|
||||
if (mUserPassword != null) {
|
||||
|
@@ -151,6 +151,11 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setForBiometrics(boolean forBiometrics) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
|
||||
mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
|
||||
return this;
|
||||
@@ -190,12 +195,16 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
final boolean forFace = getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
final boolean forBiometrics = getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
|
||||
CharSequence msg = getText(R.string.lockpassword_choose_your_screen_lock_header);
|
||||
if (forFingerprint) {
|
||||
msg = getText(R.string.lockpassword_choose_your_password_header_for_fingerprint);
|
||||
} else if (forFace) {
|
||||
msg = getText(R.string.lockpassword_choose_your_password_header_for_face);
|
||||
} else if (forBiometrics) {
|
||||
msg = getText(R.string.lockpassword_choose_your_password_header_for_biometrics);
|
||||
}
|
||||
|
||||
setTitle(msg);
|
||||
@@ -232,6 +241,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private GlifLayout mLayout;
|
||||
protected boolean mForFingerprint;
|
||||
protected boolean mForFace;
|
||||
protected boolean mForBiometrics;
|
||||
|
||||
private LockscreenCredential mFirstPassword;
|
||||
private RecyclerView mPasswordRestrictionView;
|
||||
@@ -254,9 +264,11 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
R.string.lockpassword_choose_your_screen_lock_header, // password
|
||||
R.string.lockpassword_choose_your_password_header_for_fingerprint,
|
||||
R.string.lockpassword_choose_your_password_header_for_face,
|
||||
R.string.lockpassword_choose_your_password_header_for_biometrics,
|
||||
R.string.lockpassword_choose_your_screen_lock_header, // pin
|
||||
R.string.lockpassword_choose_your_pin_header_for_fingerprint,
|
||||
R.string.lockpassword_choose_your_pin_header_for_face,
|
||||
R.string.lockpassword_choose_your_pin_header_for_biometrics,
|
||||
R.string.lockpassword_choose_your_password_message, // added security message
|
||||
R.string.lock_settings_picker_biometrics_added_security_message,
|
||||
R.string.lockpassword_choose_your_pin_message,
|
||||
@@ -267,6 +279,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
R.string.lockpassword_confirm_your_password_header,
|
||||
R.string.lockpassword_confirm_your_password_header,
|
||||
R.string.lockpassword_confirm_your_password_header,
|
||||
R.string.lockpassword_confirm_your_password_header,
|
||||
R.string.lockpassword_confirm_your_pin_header,
|
||||
R.string.lockpassword_confirm_your_pin_header,
|
||||
R.string.lockpassword_confirm_your_pin_header,
|
||||
R.string.lockpassword_confirm_your_pin_header,
|
||||
@@ -280,6 +294,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
R.string.lockpassword_confirm_passwords_dont_match,
|
||||
R.string.lockpassword_confirm_passwords_dont_match,
|
||||
R.string.lockpassword_confirm_passwords_dont_match,
|
||||
R.string.lockpassword_confirm_passwords_dont_match,
|
||||
R.string.lockpassword_confirm_pins_dont_match,
|
||||
R.string.lockpassword_confirm_pins_dont_match,
|
||||
R.string.lockpassword_confirm_pins_dont_match,
|
||||
R.string.lockpassword_confirm_pins_dont_match,
|
||||
@@ -289,18 +305,22 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
0,
|
||||
R.string.lockpassword_confirm_label);
|
||||
|
||||
Stage(int hintInAlpha, int hintInAlphaForFingerprint, int hintInAlphaForFace,
|
||||
int hintInNumeric, int hintInNumericForFingerprint, int hintInNumericForFace,
|
||||
Stage(int hintInAlpha,
|
||||
int hintInAlphaForFingerprint, int hintInAlphaForFace, int hintInAlphaForBiometrics,
|
||||
int hintInNumeric,
|
||||
int hintInNumericForFingerprint, int hintInNumericForFace, int hintInNumericForBiometrics,
|
||||
int messageInAlpha, int messageInAlphaForBiometrics,
|
||||
int messageInNumeric, int messageInNumericForBiometrics,
|
||||
int nextButtonText) {
|
||||
this.alphaHint = hintInAlpha;
|
||||
this.alphaHintForFingerprint = hintInAlphaForFingerprint;
|
||||
this.alphaHintForFace = hintInAlphaForFace;
|
||||
this.alphaHintForBiometrics = hintInAlphaForBiometrics;
|
||||
|
||||
this.numericHint = hintInNumeric;
|
||||
this.numericHintForFingerprint = hintInNumericForFingerprint;
|
||||
this.numericHintForFace = hintInNumericForFace;
|
||||
this.numericHintForBiometrics = hintInNumericForBiometrics;
|
||||
|
||||
this.alphaMessage = messageInAlpha;
|
||||
this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
|
||||
@@ -312,16 +332,19 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
public static final int TYPE_NONE = 0;
|
||||
public static final int TYPE_FINGERPRINT = 1;
|
||||
public static final int TYPE_FACE = 2;
|
||||
public static final int TYPE_BIOMETRIC = 3;
|
||||
|
||||
// Password
|
||||
public final int alphaHint;
|
||||
public final int alphaHintForFingerprint;
|
||||
public final int alphaHintForFace;
|
||||
public final int alphaHintForBiometrics;
|
||||
|
||||
// PIN
|
||||
public final int numericHint;
|
||||
public final int numericHintForFingerprint;
|
||||
public final int numericHintForFace;
|
||||
public final int numericHintForBiometrics;
|
||||
|
||||
public final int alphaMessage;
|
||||
public final int alphaMessageForBiometrics;
|
||||
@@ -335,6 +358,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return alphaHintForFingerprint;
|
||||
} else if (type == TYPE_FACE) {
|
||||
return alphaHintForFace;
|
||||
} else if (type == TYPE_BIOMETRIC) {
|
||||
return alphaHintForBiometrics;
|
||||
} else {
|
||||
return alphaHint;
|
||||
}
|
||||
@@ -343,6 +368,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return numericHintForFingerprint;
|
||||
} else if (type == TYPE_FACE) {
|
||||
return numericHintForFace;
|
||||
} else if (type == TYPE_BIOMETRIC) {
|
||||
return numericHintForBiometrics;
|
||||
} else {
|
||||
return numericHint;
|
||||
}
|
||||
@@ -376,6 +403,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mForBiometrics = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
mMinComplexity = intent.getIntExtra(
|
||||
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
|
||||
@@ -454,6 +483,8 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
|
||||
} else if (mForFace) {
|
||||
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
|
||||
} else if (mForBiometrics) {
|
||||
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_lock));
|
||||
}
|
||||
|
||||
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
|
||||
@@ -546,9 +577,15 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
|
||||
protected int getStageType() {
|
||||
return mForFingerprint ? Stage.TYPE_FINGERPRINT :
|
||||
mForFace ? Stage.TYPE_FACE :
|
||||
Stage.TYPE_NONE;
|
||||
if (mForFingerprint) {
|
||||
return Stage.TYPE_FINGERPRINT;
|
||||
} else if (mForFace) {
|
||||
return Stage.TYPE_FACE;
|
||||
} else if (mForBiometrics) {
|
||||
return Stage.TYPE_BIOMETRIC;
|
||||
} else {
|
||||
return Stage.TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupPasswordRequirementsView(View view) {
|
||||
|
@@ -81,7 +81,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
* behavior. So, now an activity does not finish itself until it gets this
|
||||
* result.
|
||||
*/
|
||||
static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
public static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
|
||||
private static final String TAG = "ChooseLockPattern";
|
||||
|
||||
@@ -134,6 +134,11 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setForBiometrics(boolean forBiometrics) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the launch such that at the end of the pattern enrollment, one of its
|
||||
* managed profile (specified by {@code profileId}) will have its lockscreen unified
|
||||
@@ -455,6 +460,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
protected int mUserId;
|
||||
protected boolean mForFingerprint;
|
||||
protected boolean mForFace;
|
||||
protected boolean mForBiometrics;
|
||||
|
||||
private static final String KEY_UI_STAGE = "uiStage";
|
||||
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
|
||||
@@ -488,6 +494,8 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mForBiometrics = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -506,6 +514,8 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
layout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
|
||||
} else if (mForFace) {
|
||||
layout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
|
||||
} else if (mForBiometrics) {
|
||||
layout.setIcon(getActivity().getDrawable(R.drawable.ic_lock));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,8 +742,8 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
} else {
|
||||
mHeaderText.setText(stage.headerMessage);
|
||||
}
|
||||
final boolean forBiometrics = mForFingerprint || mForFace;
|
||||
int message = forBiometrics ? stage.messageForBiometrics : stage.message;
|
||||
final boolean forAnyBiometric = mForFingerprint || mForFace || mForBiometrics;
|
||||
int message = forAnyBiometric ? stage.messageForBiometrics : stage.message;
|
||||
if (message == ID_EMPTY_MESSAGE) {
|
||||
mMessageText.setText("");
|
||||
} else {
|
||||
@@ -756,7 +766,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
mHeaderText.setTextColor(mDefaultHeaderColorList);
|
||||
}
|
||||
|
||||
if (stage == Stage.NeedToConfirm && forBiometrics) {
|
||||
if (stage == Stage.NeedToConfirm && forAnyBiometric) {
|
||||
mHeaderText.setText("");
|
||||
mTitleText.setText(R.string.lockpassword_draw_your_pattern_again_header);
|
||||
}
|
||||
|
@@ -50,8 +50,12 @@ public final class ChooseLockSettingsHelper {
|
||||
public static final String EXTRA_KEY_FORCE_VERIFY = "force_verify";
|
||||
// Gatekeeper HardwareAuthToken
|
||||
public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
|
||||
// For the fingerprint-only path
|
||||
public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
|
||||
// For the face-only path
|
||||
public static final String EXTRA_KEY_FOR_FACE = "for_face";
|
||||
// For the paths where multiple biometric sensors exist
|
||||
public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics";
|
||||
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
|
||||
public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
|
||||
public static final String EXTRA_KEY_REQUEST_GK_PW_HANDLE = "request_gk_pw_handle";
|
||||
|
@@ -111,28 +111,49 @@ final class SetNewPasswordController {
|
||||
*/
|
||||
public void dispatchSetNewPasswordIntent() {
|
||||
final Bundle extras;
|
||||
// TODO: handle the case with multiple biometrics, perhaps take an arg for biometric type?
|
||||
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
|
||||
&& mFaceManager != null
|
||||
&& mFaceManager.isHardwareDetected()
|
||||
&& !mFaceManager.hasEnrolledTemplates(mTargetUserId)
|
||||
&& !isFaceDisabledByAdmin()) {
|
||||
extras = getFaceChooseLockExtras();
|
||||
} else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
|
||||
&& mFingerprintManager != null
|
||||
|
||||
final boolean hasFeatureFingerprint = mPackageManager
|
||||
.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
|
||||
final boolean hasFeatureFace = mPackageManager
|
||||
.hasSystemFeature(PackageManager.FEATURE_FACE);
|
||||
|
||||
final boolean shouldShowFingerprintEnroll = mFingerprintManager != null
|
||||
&& mFingerprintManager.isHardwareDetected()
|
||||
&& !mFingerprintManager.hasEnrolledFingerprints(mTargetUserId)
|
||||
&& !isFingerprintDisabledByAdmin()) {
|
||||
&& !isFingerprintDisabledByAdmin();
|
||||
final boolean shouldShowFaceEnroll = mFaceManager != null
|
||||
&& mFaceManager.isHardwareDetected()
|
||||
&& !mFaceManager.hasEnrolledTemplates(mTargetUserId)
|
||||
&& !isFaceDisabledByAdmin();
|
||||
|
||||
if (hasFeatureFace && shouldShowFaceEnroll
|
||||
&& hasFeatureFingerprint && shouldShowFingerprintEnroll) {
|
||||
extras = getBiometricChooseLockExtras();
|
||||
} else if (hasFeatureFace && shouldShowFaceEnroll) {
|
||||
extras = getFaceChooseLockExtras();
|
||||
} else if (hasFeatureFingerprint && shouldShowFingerprintEnroll) {
|
||||
extras = getFingerprintChooseLockExtras();
|
||||
} else {
|
||||
extras = new Bundle();
|
||||
}
|
||||
|
||||
// No matter we show fingerprint options or not, we should tell the next activity which
|
||||
// user is setting new password.
|
||||
extras.putInt(Intent.EXTRA_USER_ID, mTargetUserId);
|
||||
mUi.launchChooseLock(extras);
|
||||
}
|
||||
|
||||
private Bundle getBiometricChooseLockExtras() {
|
||||
Bundle chooseLockExtras = new Bundle();
|
||||
chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
chooseLockExtras.putBoolean(
|
||||
ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
|
||||
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
|
||||
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, true);
|
||||
return chooseLockExtras;
|
||||
}
|
||||
|
||||
private Bundle getFingerprintChooseLockExtras() {
|
||||
Bundle chooseLockExtras = new Bundle();
|
||||
chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
|
||||
|
@@ -200,8 +200,9 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
|
||||
/* isPatternMode= */ false,
|
||||
/* isAlphaMode= */ false,
|
||||
/* isFingerprintSupported= */ false,
|
||||
/* isFaceSupported= */ false
|
||||
/* forFingerprint= */ false,
|
||||
/* forFace= */ false,
|
||||
/* forBiometrics= */ false
|
||||
);
|
||||
dialog.show(getFragmentManager());
|
||||
return true;
|
||||
@@ -242,7 +243,7 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
}
|
||||
|
||||
private boolean isForBiometric() {
|
||||
return mForFingerprint || mForFace;
|
||||
return mForFingerprint || mForFace || mForBiometrics;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -104,18 +104,23 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
|
||||
@Override
|
||||
protected void onSkipOrClearButtonClick(View view) {
|
||||
if (mLeftButtonIsSkip) {
|
||||
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
|
||||
final Intent intent = getActivity().getIntent();
|
||||
final boolean frpSupported = intent
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false);
|
||||
final boolean forFingerprint = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
final boolean forFace = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
final boolean forBiometrics = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
|
||||
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
|
||||
frpSupported,
|
||||
/* isPatternMode= */ false,
|
||||
mIsAlphaMode,
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
|
||||
false),
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false)
|
||||
|
||||
);
|
||||
forFingerprint,
|
||||
forFace,
|
||||
forBiometrics);
|
||||
dialog.show(getFragmentManager());
|
||||
return;
|
||||
}
|
||||
|
@@ -90,18 +90,23 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
|
||||
@Override
|
||||
protected void onSkipOrClearButtonClick(View view) {
|
||||
if (mLeftButtonIsSkip) {
|
||||
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
|
||||
final Intent intent = getActivity().getIntent();
|
||||
final boolean frpSupported = intent
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false);
|
||||
final boolean forFingerprint = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
final boolean forFace = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
final boolean forBiometrics = intent
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
|
||||
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
|
||||
frpSupported,
|
||||
/* isPatternMode= */ true,
|
||||
/* isAlphaMode= */ false,
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
|
||||
false),
|
||||
getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false)
|
||||
|
||||
);
|
||||
forFingerprint,
|
||||
forFace,
|
||||
forBiometrics);
|
||||
dialog.show(getFragmentManager());
|
||||
return;
|
||||
}
|
||||
|
@@ -43,14 +43,16 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
|
||||
public static final int RESULT_SKIP = Activity.RESULT_FIRST_USER + 10;
|
||||
|
||||
public static SetupSkipDialog newInstance(boolean isFrpSupported, boolean isPatternMode,
|
||||
boolean isAlphanumericMode, boolean isFingerprintSupported, boolean isFaceSupported) {
|
||||
boolean isAlphanumericMode, boolean forFingerprint, boolean forFace,
|
||||
boolean forBiometrics) {
|
||||
SetupSkipDialog dialog = new SetupSkipDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(ARG_FRP_SUPPORTED, isFrpSupported);
|
||||
args.putBoolean(ARG_LOCK_TYPE_PATTERN, isPatternMode);
|
||||
args.putBoolean(ARG_LOCK_TYPE_ALPHANUMERIC, isAlphanumericMode);
|
||||
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, isFingerprintSupported);
|
||||
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, isFaceSupported);
|
||||
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
|
||||
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
|
||||
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
@@ -68,11 +70,13 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
|
||||
@NonNull
|
||||
public AlertDialog.Builder onCreateDialogBuilder() {
|
||||
Bundle args = getArguments();
|
||||
final boolean isFaceSupported =
|
||||
final boolean forFace =
|
||||
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE);
|
||||
final boolean isFingerprintSupported =
|
||||
final boolean forFingerprint =
|
||||
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT);
|
||||
if (isFaceSupported || isFingerprintSupported) {
|
||||
final boolean forBiometrics =
|
||||
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS);
|
||||
if (forFace || forFingerprint || forBiometrics) {
|
||||
final int titleId;
|
||||
|
||||
if (args.getBoolean(ARG_LOCK_TYPE_PATTERN)) {
|
||||
@@ -82,13 +86,20 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
|
||||
R.string.lock_screen_password_skip_title : R.string.lock_screen_pin_skip_title;
|
||||
}
|
||||
|
||||
final int msgResId;
|
||||
if (forBiometrics) {
|
||||
msgResId = R.string.biometrics_lock_screen_setup_skip_dialog_text;
|
||||
} else if (forFace) {
|
||||
msgResId = R.string.face_lock_screen_setup_skip_dialog_text;
|
||||
} else {
|
||||
msgResId = R.string.fingerprint_lock_screen_setup_skip_dialog_text;
|
||||
}
|
||||
|
||||
return new AlertDialog.Builder(getContext())
|
||||
.setPositiveButton(R.string.skip_lock_screen_dialog_button_label, this)
|
||||
.setNegativeButton(R.string.cancel_lock_screen_dialog_button_label, this)
|
||||
.setTitle(titleId)
|
||||
.setMessage(isFaceSupported ?
|
||||
R.string.face_lock_screen_setup_skip_dialog_text :
|
||||
R.string.fingerprint_lock_screen_setup_skip_dialog_text);
|
||||
.setMessage(msgResId);
|
||||
} else {
|
||||
return new AlertDialog.Builder(getContext())
|
||||
.setPositiveButton(R.string.skip_anyway_button_label, this)
|
||||
|
@@ -23,6 +23,8 @@ import static org.robolectric.RuntimeEnvironment.application;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
@@ -53,6 +55,8 @@ import org.robolectric.shadows.ShadowActivity;
|
||||
import org.robolectric.shadows.ShadowActivity.IntentForResult;
|
||||
import org.robolectric.shadows.ShadowKeyguardManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowFingerprintManager.class,
|
||||
@@ -69,6 +73,14 @@ public class SetupFingerprintEnrollIntroductionTest {
|
||||
Shadows.shadowOf(application.getPackageManager())
|
||||
.setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true);
|
||||
|
||||
final FingerprintSensorProperties prop = new FingerprintSensorProperties(0 /* sensorId */,
|
||||
FingerprintSensorProperties.TYPE_REAR,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */,
|
||||
5 /* maxTemplatesAllowed */);
|
||||
final ArrayList<FingerprintSensorProperties> props = new ArrayList<>();
|
||||
props.add(prop);
|
||||
ShadowFingerprintManager.setSensorProperties(props);
|
||||
|
||||
FakeFeatureFactory.setupForTest();
|
||||
|
||||
final Intent intent = new Intent();
|
||||
|
@@ -46,7 +46,7 @@ public class SetupSkipDialogTest {
|
||||
@Test
|
||||
public void frpMessages_areShownCorrectly_whenNotSupported() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(false, false, false, false, false);
|
||||
SetupSkipDialog.newInstance(false, false, false, false, false, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -61,7 +61,7 @@ public class SetupSkipDialogTest {
|
||||
@Test
|
||||
public void frpMessages_areShownCorrectly_whenSupported() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, false, false, false);
|
||||
SetupSkipDialog.newInstance(true, false, false, false, false, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -76,7 +76,7 @@ public class SetupSkipDialogTest {
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPinSetupForFace_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, false, false, true);
|
||||
SetupSkipDialog.newInstance(true, false, false, false, true, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -85,13 +85,14 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pin_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(false, true, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPasswordSetupForFace_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, true, false, true);
|
||||
SetupSkipDialog.newInstance(true, false, true, false, true, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -100,13 +101,14 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_password_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(false, true, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPatternSetupForFace_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, true, false, false, true);
|
||||
SetupSkipDialog.newInstance(true, true, false, false, true, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -115,13 +117,14 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pattern_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(false, true, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPinSetupForFingerprint_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, false, true, false);
|
||||
SetupSkipDialog.newInstance(true, false, false, true, false, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -130,13 +133,14 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pin_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(true)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(true, false, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPasswordSetupForFingerprint_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, true, true, false);
|
||||
SetupSkipDialog.newInstance(true, false, true, true, false, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -145,13 +149,14 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_password_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(true)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(true, false, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPatternSetupForFingerprint_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, true, false, true, false);
|
||||
SetupSkipDialog.newInstance(true, true, false, true, false, false);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
@@ -160,13 +165,71 @@ public class SetupSkipDialogTest {
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pattern_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(true)).isEqualTo(shadowAlertDialog.getMessage());
|
||||
assertThat(getSkipDialogMessage(true, false, false))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
public String getSkipDialogMessage(boolean isFingerprintSupported) {
|
||||
return String.format(
|
||||
mActivity.getString(isFingerprintSupported ?
|
||||
R.string.fingerprint_lock_screen_setup_skip_dialog_text :
|
||||
R.string.face_lock_screen_setup_skip_dialog_text));
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPinSetupForBiometrics_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, false, false, false, true);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(alertDialog).isNotNull();
|
||||
ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pin_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false, false, true))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPasswordSetupForBiometrics_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, false, true, false, false, true);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(alertDialog).isNotNull();
|
||||
ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
|
||||
assertThat(mActivity.getString(R.string.lock_screen_password_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false, false, true))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogMessage_whenSkipPatternSetupForBiometrics_shouldShownCorrectly() {
|
||||
SetupSkipDialog setupSkipDialog =
|
||||
SetupSkipDialog.newInstance(true, true, false, false, false, true);
|
||||
setupSkipDialog.show(mActivity.getSupportFragmentManager());
|
||||
|
||||
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(alertDialog).isNotNull();
|
||||
ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
|
||||
assertThat(mActivity.getString(R.string.lock_screen_pattern_skip_title)).isEqualTo(
|
||||
shadowAlertDialog.getTitle());
|
||||
|
||||
assertThat(getSkipDialogMessage(false, false, true))
|
||||
.isEqualTo(shadowAlertDialog.getMessage());
|
||||
}
|
||||
|
||||
public String getSkipDialogMessage(boolean forFingerprint, boolean forFace,
|
||||
boolean forBiometrics) {
|
||||
final int resId;
|
||||
if (forFingerprint) {
|
||||
resId = R.string.fingerprint_lock_screen_setup_skip_dialog_text;
|
||||
} else if (forFace) {
|
||||
resId = R.string.face_lock_screen_setup_skip_dialog_text;
|
||||
} else if (forBiometrics) {
|
||||
resId = R.string.biometrics_lock_screen_setup_skip_dialog_text;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return String.format(mActivity.getString(resId));
|
||||
}
|
||||
}
|
||||
|
@@ -18,22 +18,30 @@ package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Implements(FingerprintManager.class)
|
||||
public class ShadowFingerprintManager extends org.robolectric.shadows.ShadowFingerprintManager {
|
||||
|
||||
private static List<FingerprintSensorProperties> sFingerprintSensorProperties;
|
||||
|
||||
public static void setSensorProperties(List<FingerprintSensorProperties> props) {
|
||||
sFingerprintSensorProperties = props;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected List<Fingerprint> getEnrolledFingerprints(int userId) {
|
||||
return getEnrolledFingerprints();
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected long generateChallengeBlocking() {
|
||||
return 0L;
|
||||
protected List<FingerprintSensorProperties> getSensorProperties() {
|
||||
return sFingerprintSensorProperties;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user