--- /dev/null
+<?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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/face_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:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="@dimen/suw_description_glif_margin_top"
android:paddingBottom="@dimen/suw_description_glif_margin_bottom_lists"
- android:text="@string/lock_settings_picker_fingerprint_added_security_message" />
+ android:text="@string/lock_settings_picker_biometrics_added_security_message" />
<string name="setup_lock_settings_picker_title" product="default">Protect your phone</string>
<!-- Message shown when setting up screen lock, prompting user to choose the their backup screen lock method [CHAR LIMIT=NONE] -->
- <string name="lock_settings_picker_fingerprint_added_security_message">For added security, set a backup screen lock</string>
+ <string name="lock_settings_picker_biometrics_added_security_message">For added security, set a backup screen lock</string>
<!-- 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. (tablet) [CHAR LIMIT=NONE] -->
<string name="setup_lock_settings_picker_message" product="tablet">Prevent others from using this tablet 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/fallbakc screen lock method for face authentication. Users can choose to use this method to unlock the screen instead of face authentication, or when face authentication is not accepted. [CHAR LIMIT=80] -->
+ <string name="lock_settings_picker_face_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>
<!-- 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 authentication [CHAR LIMIT=45]-->
+ <string name="face_unlock_set_unlock_pattern">Face authentication + Pattern</string>
+
+ <!-- Title for preference that guides the user through creating a backup unlock PIN for face authentication [CHAR LIMIT=45]-->
+ <string name="face_unlock_set_unlock_pin">Face authentication + PIN</string>
+
+ <!-- Title for preference that guides the user through creating a backup unlock password for face authentication [CHAR LIMIT=45]-->
+ <string name="face_unlock_set_unlock_password">Face authentication + Password</string>
+
+ <!-- Title for preference that guides the user to skip face authentication setup [CHAR LIMIT=60]-->
+ <string name="face_unlock_skip_face">Continue without face authentication</string>
+
+ <!-- Message shown in screen lock picker while setting up the new screen lock with face authentication 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>
+
<!-- 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>
<string name="lockpassword_pin_set_toast">PIN has been set</string>
<!-- Toast shown if setting pattern was successful -->
<string name="lockpassword_pattern_set_toast">Pattern has been set</string>
+ <!-- Header on first screen of choose password/PIN as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
+ <string name="lockpassword_choose_your_password_header_for_face">To use face authentication, set password</string>
+ <!-- Header on first screen of choose pattern as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
+ <string name="lockpassword_choose_your_pattern_header_for_face">To use face authentication, set pattern</string>
+ <!-- Header on first screen of choose password/PIN as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
+ <string name="lockpassword_choose_your_pin_header_for_face">To use face authentication, set PIN</string>
<!-- Message to be used to explain the user that he needs to enter his pattern to continue a
particular operation. [CHAR LIMIT=70]-->
<!-- 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 fingerprint unlock. [CHAR LIMIT=NONE] -->
<string name="encryption_interstitial_message_password_for_fingerprint">In addition to using your fingerprint 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 face unlock. [CHAR LIMIT=NONE] -->
+ <string name="encryption_interstitial_message_pin_for_face">In addition to using your face 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 face unlock. [CHAR LIMIT=NONE] -->
+ <string name="encryption_interstitial_message_pattern_for_face">In addition to using your face 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 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>
+
<!-- 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] -->
<item name="android:lineSpacingMultiplier">1.2</item>
</style>
+ <style name="FaceHeaderStyle" parent="android:style/TextAppearance.Material.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="RingProgressBarStyle" parent="android:style/Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminate">false</item>
<item name="android:max">10000</item>
android:title="@string/fingerprint_unlock_skip_fingerprint"
android:persistent="false"/>
+ <com.android.settingslib.RestrictedPreference
+ android:key="unlock_skip_face"
+ android:title="@string/face_unlock_skip_face"
+ android:persistent="false"/>
+
</PreferenceScreen>
mRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_require_password);
mDontRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_dont_require_password);
- boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
+ final boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
+ final boolean forFace = getActivity().getIntent()
+ .getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
Intent intent = getActivity().getIntent();
mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_pattern_for_fingerprint :
+ forFace ?
+ R.string.encryption_interstitial_message_pattern_for_face :
R.string.encryption_interstitial_message_pattern;
break;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_pin_for_fingerprint :
+ forFace ?
+ R.string.encryption_interstitial_message_pin_for_face :
R.string.encryption_interstitial_message_pin;
break;
default:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_password_for_fingerprint :
+ forFace ?
+ R.string.encryption_interstitial_message_password_for_face :
R.string.encryption_interstitial_message_password;
break;
}
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment
.RESULT_FINISHED;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreference;
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 PASSWORD_CONFIRMED = "password_confirmed";
private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
private ChooseLockGenericController mController;
protected boolean mForFingerprint = false;
+ protected boolean mForFace = false;
@Override
public int getMetricsCategory() {
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
mForFingerprint = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
+ mForFace = getActivity().getIntent().getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
mUserManager = UserManager.get(getActivity());
((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description))
.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);
+ }
}
}
// unlock method to an insecure one
showFactoryResetProtectionWarningDialog(key);
return true;
- } else if (KEY_SKIP_FINGERPRINT.equals(key)) {
+ } else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)) {
Intent chooseLockGenericIntent = new Intent(getActivity(),
ChooseLockGeneric.InternalActivity.class);
chooseLockGenericIntent.setAction(getIntent().getAction());
unlockMethodIntent);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
mForFingerprint);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE,
+ mForFace);
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
startActivityForResult(
intent,
// Used for testing purposes
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(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
}
setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin);
setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.fingerprint_unlock_set_unlock_password);
+ } else if (mForFace) {
+ setPreferenceTitle(ScreenLockType.PATTERN,
+ R.string.face_unlock_set_unlock_pattern);
+ setPreferenceTitle(ScreenLockType.PIN, R.string.face_unlock_set_unlock_pin);
+ setPreferenceTitle(ScreenLockType.PASSWORD,
+ R.string.face_unlock_set_unlock_password);
}
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
if (!(mForFingerprint && mIsSetNewPassword)) {
removePreference(KEY_SKIP_FINGERPRINT);
}
+ if (!(mForFace && mIsSetNewPassword)) {
+ removePreference(KEY_SKIP_FACE);
+ }
}
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
new ChooseLockPassword.IntentBuilder(getContext())
.setPasswordQuality(quality)
.setForFingerprint(mForFingerprint)
+ .setForFace(mForFace)
.setUserId(mUserId);
if (mHasChallenge) {
builder.setChallenge(mChallenge);
ChooseLockPattern.IntentBuilder builder =
new ChooseLockPattern.IntentBuilder(getContext())
.setForFingerprint(mForFingerprint)
+ .setForFace(mForFace)
.setUserId(mUserId);
if (mHasChallenge) {
builder.setChallenge(mChallenge);
return this;
}
+ public IntentBuilder setForFace(boolean forFace) {
+ mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
+ return this;
+ }
+
public Intent build() {
return mIntent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- boolean forFingerprint = getIntent()
+ final boolean forFingerprint = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
- CharSequence msg = getText(forFingerprint
- ? R.string.lockpassword_choose_your_password_header_for_fingerprint
- : R.string.lockpassword_choose_your_screen_lock_header);
+ final boolean forFace = getIntent()
+ .getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, 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);
+ }
+
setTitle(msg);
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
layout.setFitsSystemWindows(false);
private PasswordRequirementAdapter mPasswordRequirementAdapter;
private GlifLayout mLayout;
protected boolean mForFingerprint;
+ protected boolean mForFace;
private String mFirstPin;
private RecyclerView mPasswordRestrictionView;
protected enum Stage {
Introduction(
- R.string.lockpassword_choose_your_screen_lock_header,
+ R.string.lockpassword_choose_your_screen_lock_header, // password
R.string.lockpassword_choose_your_password_header_for_fingerprint,
- R.string.lockpassword_choose_your_screen_lock_header,
+ R.string.lockpassword_choose_your_password_header_for_face,
+ R.string.lockpassword_choose_your_screen_lock_header, // pin
R.string.lockpassword_choose_your_pin_header_for_fingerprint,
- R.string.lockpassword_choose_your_password_message,
- R.string.lock_settings_picker_fingerprint_added_security_message,
+ R.string.lockpassword_choose_your_pin_header_for_face,
+ 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,
- R.string.lock_settings_picker_fingerprint_added_security_message,
+ R.string.lock_settings_picker_biometrics_added_security_message,
R.string.next_label),
NeedToConfirm(
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,
0,
ConfirmWrong(
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,
0,
0,
R.string.lockpassword_confirm_label);
- Stage(int hintInAlpha, int hintInAlphaForFingerprint,
- int hintInNumeric, int hintInNumericForFingerprint,
- int messageInAlpha, int messageInAlphaForFingerprint,
- int messageInNumeric, int messageInNumericForFingerprint,
+ Stage(int hintInAlpha, int hintInAlphaForFingerprint, int hintInAlphaForFace,
+ int hintInNumeric, int hintInNumericForFingerprint, int hintInNumericForFace,
+ int messageInAlpha, int messageInAlphaForBiometrics,
+ int messageInNumeric, int messageInNumericForBiometrics,
int nextButtonText) {
this.alphaHint = hintInAlpha;
this.alphaHintForFingerprint = hintInAlphaForFingerprint;
+ this.alphaHintForFace = hintInAlphaForFace;
+
this.numericHint = hintInNumeric;
this.numericHintForFingerprint = hintInNumericForFingerprint;
+ this.numericHintForFace = hintInNumericForFace;
+
this.alphaMessage = messageInAlpha;
- this.alphaMessageForFingerprint = messageInAlphaForFingerprint;
+ this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
this.numericMessage = messageInNumeric;
- this.numericMessageForFingerprint = messageInNumericForFingerprint;
+ this.numericMessageForBiometrics = messageInNumericForBiometrics;
this.buttonText = nextButtonText;
}
+ public static final int TYPE_NONE = 0;
+ public static final int TYPE_FINGERPRINT = 1;
+ public static final int TYPE_FACE = 2;
+
+ // Password
public final int alphaHint;
public final int alphaHintForFingerprint;
+ public final int alphaHintForFace;
+
+ // PIN
public final int numericHint;
public final int numericHintForFingerprint;
+ public final int numericHintForFace;
+
public final int alphaMessage;
- public final int alphaMessageForFingerprint;
+ public final int alphaMessageForBiometrics;
public final int numericMessage;
- public final int numericMessageForFingerprint;
+ public final int numericMessageForBiometrics;
public final int buttonText;
- public @StringRes int getHint(boolean isAlpha, boolean isFingerprint) {
+ public @StringRes int getHint(boolean isAlpha, int type) {
if (isAlpha) {
- return isFingerprint ? alphaHintForFingerprint : alphaHint;
+ if (type == TYPE_FINGERPRINT) {
+ return alphaHintForFingerprint;
+ } else if (type == TYPE_FACE) {
+ return alphaHintForFace;
+ } else {
+ return alphaHint;
+ }
} else {
- return isFingerprint ? numericHintForFingerprint : numericHint;
+ if (type == TYPE_FINGERPRINT) {
+ return numericHintForFingerprint;
+ } else if (type == TYPE_FACE) {
+ return numericHintForFace;
+ } else {
+ return numericHint;
+ }
}
}
- public @StringRes int getMessage(boolean isAlpha, boolean isFingerprint) {
+ public @StringRes int getMessage(boolean isAlpha, int type) {
if (isAlpha) {
- return isFingerprint ? alphaMessageForFingerprint : alphaMessage;
+ return type != TYPE_NONE ? alphaMessageForBiometrics : alphaMessage;
} else {
- return isFingerprint ? numericMessageForFingerprint : numericMessage;
+ return type != TYPE_NONE ? numericMessageForBiometrics : numericMessage;
}
}
}
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
+ mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
processPasswordRequirements(intent);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
mMessage = view.findViewById(R.id.message);
if (mForFingerprint) {
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
+ } else if (mForFace) {
+ mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
}
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
if (activity instanceof SettingsActivity) {
final SettingsActivity sa = (SettingsActivity) activity;
- int title = Stage.Introduction.getHint(mIsAlphaMode, mForFingerprint);
+ int title = Stage.Introduction.getHint(mIsAlphaMode, getStageType());
sa.setTitle(title);
mLayout.setHeaderText(title);
}
}
+ private int getStageType() {
+ return mForFingerprint ? Stage.TYPE_FINGERPRINT :
+ mForFace ? Stage.TYPE_FACE :
+ Stage.TYPE_NONE;
+ }
+
private void setupPasswordRequirementsView(View view) {
final List<Integer> passwordRequirements = new ArrayList<>();
if (mPasswordMinUpperCase > 0) {
} else {
// Hide password requirement view when we are just asking user to confirm the pw.
mPasswordRestrictionView.setVisibility(View.GONE);
- setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, mForFingerprint)));
+ setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
setNextEnabled(canInput && length >= mPasswordMinLength);
mClearButton.setEnabled(canInput && length > 0);
}
- int message = mUiStage.getMessage(mIsAlphaMode, mForFingerprint);
+ int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
if (message != 0) {
mMessage.setVisibility(View.VISIBLE);
mMessage.setText(message);
return this;
}
+ public IntentBuilder setForFace(boolean forFace) {
+ mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
+ return this;
+ }
+
public Intent build() {
return mIntent;
}
protected void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
- boolean forFingerprint = getIntent()
+ final boolean forFingerprint = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
- setTitle(forFingerprint ? R.string.lockpassword_choose_your_pattern_header_for_fingerprint
- : R.string.lockpassword_choose_your_screen_lock_header);
+ final boolean forFace = getIntent()
+ .getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
+
+ int msg = R.string.lockpassword_choose_your_screen_lock_header;
+ if (forFingerprint) {
+ msg = R.string.lockpassword_choose_your_pattern_header_for_fingerprint;
+ } else if (forFace) {
+ msg = R.string.lockpassword_choose_your_pattern_header_for_face;
+ }
+
+ setTitle(msg);
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
layout.setFitsSystemWindows(false);
}
protected enum Stage {
Introduction(
- R.string.lock_settings_picker_fingerprint_added_security_message,
+ R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Gone, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
- R.string.lock_settings_picker_fingerprint_added_security_message,
+ R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
- R.string.lock_settings_picker_fingerprint_added_security_message,
+ R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
/**
- * @param messageForFingerprint The message displayed at the top, above header for
+ * @param messageForBiometrics The message displayed at the top, above header for
* fingerprint flow.
* @param message The message displayed at the top.
* @param headerMessage The message displayed at the top.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
- Stage(int messageForFingerprint, int message, int headerMessage,
+ Stage(int messageForBiometrics, int message, int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
- this.messageForFingerprint = messageForFingerprint;
+ this.messageForBiometrics = messageForBiometrics;
this.message = message;
this.leftMode = leftMode;
this.rightMode = rightMode;
}
final int headerMessage;
- final int messageForFingerprint;
+ final int messageForBiometrics;
final int message;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
private SaveAndFinishWorker mSaveAndFinishWorker;
protected int mUserId;
protected boolean mForFingerprint;
+ protected boolean mForFace;
private static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
+ mForFace = intent.getBooleanExtra(
+ ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
}
@Override
} else {
if (mForFingerprint) {
layout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
+ } else if (mForFace) {
+ layout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
}
}
return layout;
} else {
mHeaderText.setText(stage.headerMessage);
}
- int message = mForFingerprint ? stage.messageForFingerprint : stage.message;
+ final boolean forBiometrics = mForFingerprint || mForFace;
+ int message = forBiometrics ? stage.messageForBiometrics : stage.message;
if (message == ID_EMPTY_MESSAGE) {
mMessageText.setText("");
} else {
mHeaderText.setTextColor(mDefaultHeaderColorList);
}
- if (stage == Stage.NeedToConfirm && mForFingerprint) {
+ if (stage == Stage.NeedToConfirm && forBiometrics) {
mHeaderText.setText("");
mTitleText.setText(R.string.lockpassword_draw_your_pattern_again_header);
}
public static final String EXTRA_KEY_CHALLENGE = "challenge";
public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
+ public static final String EXTRA_KEY_FOR_FACE = "for_face";
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
/**
package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.IBinder;
private final PackageManager mPackageManager;
@Nullable
private final FingerprintManager mFingerprintManager;
+ @Nullable
+ private final FaceManager mFaceManager;
private final DevicePolicyManager mDevicePolicyManager;
private final Ui mUi;
}
// Create a wrapper of FingerprintManager for testing, see IFingerPrintManager for details.
final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ final FaceManager faceManager = Utils.getFaceManagerOrNull(context);
return new SetNewPasswordController(userId,
context.getPackageManager(),
- fingerprintManager,
+ fingerprintManager, faceManager,
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE), ui);
}
int targetUserId,
PackageManager packageManager,
FingerprintManager fingerprintManager,
+ FaceManager faceManager,
DevicePolicyManager devicePolicyManager,
Ui ui) {
mTargetUserId = targetUserId;
mPackageManager = checkNotNull(packageManager);
mFingerprintManager = fingerprintManager;
+ mFaceManager = faceManager;
mDevicePolicyManager = checkNotNull(devicePolicyManager);
mUi = checkNotNull(ui);
}
*/
public void dispatchSetNewPasswordIntent() {
final Bundle extras;
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
+ // 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.hasEnrolledFaces(mTargetUserId)
+ && !isFaceDisabledByAdmin()) {
+ extras = getFaceChooseLockExtras();
+ } else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
&& mFingerprintManager != null
&& mFingerprintManager.isHardwareDetected()
&& !mFingerprintManager.hasEnrolledFingerprints(mTargetUserId)
return chooseLockExtras;
}
+ private Bundle getFaceChooseLockExtras() {
+ Bundle chooseLockExtras = new Bundle();
+ long challenge = mFaceManager.preEnroll();
+ chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
+ PASSWORD_QUALITY_SOMETHING);
+ chooseLockExtras.putBoolean(
+ ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
+ chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
+ chooseLockExtras.putLong(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+ chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, true);
+ return chooseLockExtras;
+ }
+
private boolean isFingerprintDisabledByAdmin() {
int disabledFeatures =
mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
return (disabledFeatures & KEYGUARD_DISABLE_FINGERPRINT) != 0;
}
+
+ private boolean isFaceDisabledByAdmin() {
+ int disabledFeatures =
+ mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
+ return (disabledFeatures & KEYGUARD_DISABLE_FACE) != 0;
+ }
}
@Override
protected void addHeaderView() {
- if (mForFingerprint) {
- setHeaderView(R.layout.setup_choose_lock_generic_fingerprint_header);
+ if (mForFingerprint || mForFace) {
+ setHeaderView(R.layout.setup_choose_lock_generic_biometrics_header);
} else {
setHeaderView(R.layout.setup_choose_lock_generic_header);
}
package com.android.settings.password;
+import static android.content.pm.PackageManager.FEATURE_FACE;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment
.HIDE_DISABLED_PREFS;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment
.MINIMUM_QUALITY_KEY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
private static final int CURRENT_USER_ID = 101;
private static final long FINGERPRINT_CHALLENGE = -9876512313131L;
+ private static final long FACE_CHALLENGE = 1352057789L;
@Mock
private PackageManager mPackageManager;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
+ private FaceManager mFaceManager;
+ @Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private SetNewPasswordController.Ui mUi;
public void setUp() {
MockitoAnnotations.initMocks(this);
mSetNewPasswordController = new SetNewPasswordController(
- CURRENT_USER_ID, mPackageManager, mFingerprintManager, mDevicePolicyManager, mUi);
+ CURRENT_USER_ID, mPackageManager, mFingerprintManager, mFaceManager,
+ mDevicePolicyManager, mUi);
when(mFingerprintManager.preEnroll()).thenReturn(FINGERPRINT_CHALLENGE);
when(mPackageManager.hasSystemFeature(eq(FEATURE_FINGERPRINT))).thenReturn(true);
+
+ when(mFaceManager.preEnroll()).thenReturn(FACE_CHALLENGE);
+ when(mPackageManager.hasSystemFeature(eq(FEATURE_FACE))).thenReturn(true);
}
@Test
}
@Test
+ public void launchChooseLockWithFace() {
+ // GIVEN the device supports face.
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ // GIVEN there are no enrolled face.
+ when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(false);
+ // GIVEN DPC does not disallow face for keyguard usage.
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
+ .thenReturn(0);
+
+ // WHEN the controller dispatches a set new password intent.
+ mSetNewPasswordController.dispatchSetNewPasswordIntent();
+
+ // THEN the choose lock activity is launched with face extras.
+ ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
+ // THEN the extras have all values for face setup.
+ compareFaceExtras(bundleArgumentCaptor.getValue());
+ }
+
+ @Test
public void launchChooseLockWithoutFingerprint_noFingerprintFeature() {
// GIVEN the device does NOT support fingerprint feature.
when(mPackageManager.hasSystemFeature(eq(FEATURE_FINGERPRINT))).thenReturn(false);
}
@Test
+ public void launchChooseLockWithoutFace_no_FaceFeature() {
+ // GIVEN the device does NOT support face feature.
+ when(mPackageManager.hasSystemFeature(eq(FEATURE_FACE))).thenReturn(false);
+
+ // WHEN the controller dispatches a set new password intent.
+ mSetNewPasswordController.dispatchSetNewPasswordIntent();
+
+ // THEN the choose lock activity is launched without face extras.
+ ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
+ assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
+ }
+
+ @Test
public void launchChooseLockWithoutFingerprint_noFingerprintSensor() {
// GIVEN the device does NOT support fingerprint.
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
}
@Test
+ public void launchChooseLockWithoutFace_noFaceSensor() {
+ // GIVEN the device does NOT support face.
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+ // GIVEN there are no enrolled face.
+ when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(false);
+ // GIVEN DPC does not disallow face for keyguard usage.
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
+ .thenReturn(0);
+
+ // WHEN the controller dispatches a set new password intent.
+ mSetNewPasswordController.dispatchSetNewPasswordIntent();
+
+ // THEN the choose lock activity is launched without a bundle contains user id only.
+ ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
+ assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
+ }
+
+ @Test
public void launchChooseLockWithoutFingerprint_hasFingerprintEnrolled() {
// GIVEN the device supports fingerprint.
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
}
@Test
+ public void launchChooseLockWithoutFace_hasFaceEnrolled() {
+ // GIVEN the device supports face.
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ // GIVEN there are no enrolled face.
+ when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(true);
+ // GIVEN DPC does not disallow face for keyguard usage.
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
+ .thenReturn(0);
+
+ // WHEN the controller dispatches a set new password intent.
+ mSetNewPasswordController.dispatchSetNewPasswordIntent();
+
+ // THEN the choose lock activity is launched without a bundle contains user id only.
+ ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
+ assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
+ }
+
+ @Test
public void launchChooseLockWithoutFingerprint_deviceAdminDisallowFingerprintForKeyguard() {
// GIVEN the device supports fingerprint.
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
+ @Test
+ public void launchChooseLockWithoutFace_deviceAdminDisallowFaceForKeyguard() {
+ // GIVEN the device supports face.
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ // GIVEN there is an enrolled face.
+ when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(true);
+ // GIVEN DPC disallows face for keyguard usage.
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
+
+ // WHEN the controller dispatches a set new password intent.
+ mSetNewPasswordController.dispatchSetNewPasswordIntent();
+
+ // THEN the choose lock activity is launched without a bundle contains user id only.
+ ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
+ assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
+ }
+
private void compareFingerprintExtras(Bundle actualBundle) {
assertEquals(
"Password quality must be something in order to config fingerprint.",
actualBundle.getInt(Intent.EXTRA_USER_ID));
}
+ private void compareFaceExtras(Bundle actualBundle) {
+ assertEquals(
+ "Password quality must be something in order to config face.",
+ DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
+ actualBundle.getInt(MINIMUM_QUALITY_KEY));
+ assertTrue(
+ "All disabled preference should be removed.",
+ actualBundle.getBoolean(HIDE_DISABLED_PREFS));
+ assertTrue(
+ "There must be a face challenge.",
+ actualBundle.getBoolean(EXTRA_KEY_HAS_CHALLENGE));
+ assertEquals(
+ "The face challenge must come from the FaceManager",
+ FACE_CHALLENGE,
+ actualBundle.getLong(EXTRA_KEY_CHALLENGE));
+ assertTrue(
+ "The request must be a face set up request.",
+ actualBundle.getBoolean(EXTRA_KEY_FOR_FACE));
+ assertEquals(
+ "User id must be equaled to the input one.",
+ CURRENT_USER_ID,
+ actualBundle.getInt(Intent.EXTRA_USER_ID));
+ }
+
private void assertBundleContainsUserIdOnly(Bundle actualBundle) {
assertThat(actualBundle.size()).isEqualTo(1);
assertThat(actualBundle.getInt(Intent.EXTRA_USER_ID)).isEqualTo(CURRENT_USER_ID);