Default setup wizard to use PIN

- Added "Screen lock options" button in PIN screen, controlled by
  extra EXTRA_SHOW_OPTIONS_BUTTON, which will create a dialog to ask
  the user to choose another screen lock type.
- Extracted ScreenLockType enum and ChooseLockGenericController that
  can be shared by ChooseLockGeneric and the dialog
  ChooseLockTypeDialogFragment.
- The intent extra EXTRA_SHOW_OPTIONS_BUTTON will be set if
  ChooseLockGeneric screen starts ChooseLockPassword /
  ChooseLockPattern without asking the user. (Although the extra is
  ignored by ChooseLockPattern currently)
- Fix layout alignment for the password entry field to remove the
  extra 4dp padding on the sides.

Test: cd tests/robotests && mma
Bug: 35442933
Bug: 38002299
Change-Id: I877fbe08a0c05bb97175e1cbf0260ea6dbda22e2
This commit is contained in:
Maurice Lam
2017-04-27 18:54:33 -07:00
parent 9e8a182c31
commit edb3944984
17 changed files with 1117 additions and 183 deletions

View File

@@ -43,11 +43,13 @@
android:textAppearance="?android:attr/textAppearanceMedium"/> android:textAppearance="?android:attr/textAppearanceMedium"/>
<LinearLayout <LinearLayout
android:id="@+id/password_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:layoutMode="opticalBounds"
android:paddingBottom="8dp"> android:paddingBottom="8dp">
<!-- Password entry field --> <!-- Password entry field -->
@@ -68,6 +70,15 @@
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
</LinearLayout> </LinearLayout>
<Button
android:id="@+id/screen_lock_options"
style="@style/SuwGlifButton.Tertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/setup_lock_settings_options_button_label"
android:visibility="gone" />
</LinearLayout> </LinearLayout>
</com.android.setupwizardlib.GlifLayout> </com.android.setupwizardlib.GlifLayout>

View File

@@ -18,5 +18,4 @@
android:id="@+id/description_text" android:id="@+id/description_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="4dp" android:textSize="14sp"/>
android:textSize="14sp"/>

View File

@@ -1088,6 +1088,9 @@
<!-- 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] --> <!-- 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> <string name="lock_settings_picker_fingerprint_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>
<!-- Main Security lock settings --><skip /> <!-- Main Security lock settings --><skip />
<!-- Title for PreferenceScreen to launch picker for security method when there is none [CHAR LIMIT=22] --> <!-- Title for PreferenceScreen to launch picker for security method when there is none [CHAR LIMIT=22] -->
<string name="unlock_set_unlock_launch_picker_title">Screen lock</string> <string name="unlock_set_unlock_launch_picker_title">Screen lock</string>

View File

@@ -17,6 +17,7 @@
package com.android.settings.fingerprint; package com.android.settings.fingerprint;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.UserHandle; import android.os.UserHandle;
@@ -27,13 +28,19 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SetupWizardUtils; import com.android.settings.SetupWizardUtils;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.password.SetupChooseLockGeneric; import com.android.settings.password.SetupChooseLockGeneric;
public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction { public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
@Override @Override
protected Intent getChooseLockIntent() { protected Intent getChooseLockIntent() {
Intent intent = new Intent(this, SetupChooseLockGeneric.class); Intent intent = new Intent(this, SetupChooseLockGeneric.class)
.putExtra(
LockPatternUtils.PASSWORD_TYPE_KEY,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
SetupWizardUtils.copySetupExtras(getIntent(), intent); SetupWizardUtils.copySetupExtras(getIntent(), intent);
return intent; return intent;
} }

View File

@@ -29,7 +29,6 @@ import android.app.Fragment;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.Fingerprint;
@@ -40,6 +39,7 @@ import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.security.KeyStore; import android.security.KeyStore;
import android.support.annotation.StringRes;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils; import android.text.TextUtils;
@@ -95,14 +95,9 @@ public class ChooseLockGeneric extends SettingsActivity {
} }
public static class ChooseLockGenericFragment extends SettingsPreferenceFragment { public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
private static final String TAG = "ChooseLockGenericFragment"; private static final String TAG = "ChooseLockGenericFragment";
private static final int MIN_PASSWORD_LENGTH = 4; private static final int MIN_PASSWORD_LENGTH = 4;
private static final String KEY_UNLOCK_SET_OFF = "unlock_set_off";
private static final String KEY_UNLOCK_SET_NONE = "unlock_set_none";
private static final String KEY_UNLOCK_SET_PIN = "unlock_set_pin";
private static final String KEY_UNLOCK_SET_PASSWORD = "unlock_set_password";
private static final String KEY_UNLOCK_SET_PATTERN = "unlock_set_pattern";
private static final String KEY_UNLOCK_SET_MANAGED = "unlock_set_managed";
private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint"; private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint";
private static final String PASSWORD_CONFIRMED = "password_confirmed"; private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation"; private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
@@ -112,6 +107,27 @@ public class ChooseLockGeneric extends SettingsActivity {
public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled"; public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled";
public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog"; public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog";
/**
* Boolean extra determining whether a "screen lock options" button should be shown. This
* extra is both sent and received by ChooseLockGeneric.
*
* When this extra is false, nothing will be done.
* When ChooseLockGeneric receives this extra set as true, and if ChooseLockGeneric is
* starting ChooseLockPassword or ChooseLockPattern automatically without user interaction,
* ChooseLockGeneric will set this extra to true when starting ChooseLockPassword/Pattern.
*
* This gives the user the choice to select a different screen lock type, even if
* ChooseLockGeneric selected a default.
*/
public static final String EXTRA_SHOW_OPTIONS_BUTTON = "show_options_button";
/**
* Original intent extras used to start this activity. This is passed to ChooseLockPassword
* when the "screen lock options" button is shown, so that when that button is clicked,
* ChooseLockGeneric can be relaunched with the same extras.
*/
public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras";
private static final int CONFIRM_EXISTING_REQUEST = 100; private static final int CONFIRM_EXISTING_REQUEST = 100;
private static final int ENABLE_ENCRYPTION_REQUEST = 101; private static final int ENABLE_ENCRYPTION_REQUEST = 101;
private static final int CHOOSE_LOCK_REQUEST = 102; private static final int CHOOSE_LOCK_REQUEST = 102;
@@ -136,6 +152,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private ManagedLockPasswordProvider mManagedPasswordProvider; private ManagedLockPasswordProvider mManagedPasswordProvider;
private boolean mIsSetNewPassword = false; private boolean mIsSetNewPassword = false;
private UserManager mUserManager; private UserManager mUserManager;
private ChooseLockGenericController mController;
protected boolean mForFingerprint = false; protected boolean mForFingerprint = false;
@@ -192,6 +209,7 @@ public class ChooseLockGeneric extends SettingsActivity {
UserManager.get(getActivity()), UserManager.get(getActivity()),
getArguments(), getArguments(),
getActivity().getIntent().getExtras()).getIdentifier(); getActivity().getIntent().getExtras()).getIdentifier();
mController = new ChooseLockGenericController(getContext(), mUserId);
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction) if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
&& UserManager.get(getActivity()).isManagedProfile(mUserId) && UserManager.get(getActivity()).isManagedProfile(mUserId)
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) { && mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -303,7 +321,7 @@ public class ChooseLockGeneric extends SettingsActivity {
finish(); finish();
return; return;
} }
updateUnlockMethodAndFinish(quality, disabled); updateUnlockMethodAndFinish(quality, disabled, false /* chooseLockSkipped */);
} }
} }
@@ -328,6 +346,15 @@ public class ChooseLockGeneric extends SettingsActivity {
if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) { if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
getActivity().setResult(resultCode, data); getActivity().setResult(resultCode, data);
finish(); finish();
} else {
// If PASSWORD_TYPE_KEY is set, this activity is used as a trampoline to start
// the actual password enrollment. If the result is canceled, which means the
// user pressed back, finish the activity with result canceled.
int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality != -1) {
getActivity().setResult(RESULT_CANCELED, data);
finish();
}
} }
} else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST } else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
&& resultCode == FingerprintEnrollBase.RESULT_FINISHED) { && resultCode == FingerprintEnrollBase.RESULT_FINISHED) {
@@ -374,7 +401,7 @@ public class ChooseLockGeneric extends SettingsActivity {
if (quality == -1) { if (quality == -1) {
// If caller didn't specify password quality, show UI and allow the user to choose. // If caller didn't specify password quality, show UI and allow the user to choose.
quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1); quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
quality = upgradeQuality(quality); quality = mController.upgradeQuality(quality);
final boolean hideDisabledPrefs = intent.getBooleanExtra( final boolean hideDisabledPrefs = intent.getBooleanExtra(
HIDE_DISABLED_PREFS, false); HIDE_DISABLED_PREFS, false);
final PreferenceScreen prefScreen = getPreferenceScreen(); final PreferenceScreen prefScreen = getPreferenceScreen();
@@ -387,7 +414,7 @@ public class ChooseLockGeneric extends SettingsActivity {
updateCurrentPreference(); updateCurrentPreference();
updatePreferenceSummaryIfNeeded(); updatePreferenceSummaryIfNeeded();
} else { } else {
updateUnlockMethodAndFinish(quality, false); updateUnlockMethodAndFinish(quality, false, true /* chooseLockSkipped */);
} }
} }
@@ -395,33 +422,26 @@ public class ChooseLockGeneric extends SettingsActivity {
addPreferencesFromResource(R.xml.security_settings_picker); addPreferencesFromResource(R.xml.security_settings_picker);
// Used for testing purposes // Used for testing purposes
findPreference(KEY_UNLOCK_SET_NONE).setViewId(R.id.lock_none); findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none); findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
findPreference(KEY_UNLOCK_SET_PIN).setViewId(R.id.lock_pin); findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
findPreference(KEY_UNLOCK_SET_PASSWORD).setViewId(R.id.lock_password); findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
} }
private void updatePreferenceText() { private void updatePreferenceText() {
if (mForFingerprint) { if (mForFingerprint) {
final String key[] = { KEY_UNLOCK_SET_PATTERN, setPreferenceTitle(ScreenLockType.PATTERN,
KEY_UNLOCK_SET_PIN, R.string.fingerprint_unlock_set_unlock_pattern);
KEY_UNLOCK_SET_PASSWORD }; setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin);
final int res[] = { R.string.fingerprint_unlock_set_unlock_pattern, setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.fingerprint_unlock_set_unlock_pin, R.string.fingerprint_unlock_set_unlock_password);
R.string.fingerprint_unlock_set_unlock_password };
for (int i = 0; i < key.length; i++) {
Preference pref = findPreference(key[i]);
if (pref != null) { // can be removed by device admin
pref.setTitle(res[i]);
}
}
} }
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) { if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
Preference managed = findPreference(KEY_UNLOCK_SET_MANAGED); setPreferenceTitle(ScreenLockType.MANAGED,
managed.setTitle(mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint)); mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint));
} else { } else {
removePreference(KEY_UNLOCK_SET_MANAGED); removePreference(ScreenLockType.MANAGED.preferenceKey);
} }
if (!(mForFingerprint && mIsSetNewPassword)) { if (!(mForFingerprint && mIsSetNewPassword)) {
@@ -429,6 +449,27 @@ public class ChooseLockGeneric extends SettingsActivity {
} }
} }
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
Preference preference = findPreference(lock.preferenceKey);
if (preference != null) {
preference.setTitle(title);
}
}
private void setPreferenceTitle(ScreenLockType lock, CharSequence title) {
Preference preference = findPreference(lock.preferenceKey);
if (preference != null) {
preference.setTitle(title);
}
}
private void setPreferenceSummary(ScreenLockType lock, @StringRes int summary) {
Preference preference = findPreference(lock.preferenceKey);
if (preference != null) {
preference.setSummary(summary);
}
}
private void updateCurrentPreference() { private void updateCurrentPreference() {
String currentKey = getKeyForCurrent(); String currentKey = getKeyForCurrent();
Preference preference = findPreference(currentKey); Preference preference = findPreference(currentKey);
@@ -441,39 +482,12 @@ public class ChooseLockGeneric extends SettingsActivity {
final int credentialOwner = UserManager.get(getContext()) final int credentialOwner = UserManager.get(getContext())
.getCredentialOwnerProfile(mUserId); .getCredentialOwnerProfile(mUserId);
if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) { if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) {
return KEY_UNLOCK_SET_OFF; return ScreenLockType.NONE.preferenceKey;
} }
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner)) { ScreenLockType lock =
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: ScreenLockType.fromQuality(
return KEY_UNLOCK_SET_PATTERN; mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner));
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: return lock != null ? lock.preferenceKey : null;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return KEY_UNLOCK_SET_PIN;
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
return KEY_UNLOCK_SET_PASSWORD;
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
return KEY_UNLOCK_SET_MANAGED;
case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
return KEY_UNLOCK_SET_NONE;
}
return null;
}
/** increases the quality if necessary */
private int upgradeQuality(int quality) {
quality = upgradeQualityForDPM(quality);
return quality;
}
private int upgradeQualityForDPM(int quality) {
// Compare min allowed password quality
int minQuality = mDPM.getPasswordQuality(null, mUserId);
if (quality < minQuality) {
quality = minQuality;
}
return quality;
} }
/*** /***
@@ -501,54 +515,17 @@ public class ChooseLockGeneric extends SettingsActivity {
int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId); int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId);
EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet( EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet(
getActivity(), mUserId); getActivity(), mUserId);
for (int i = entries.getPreferenceCount() - 1; i >= 0; --i) {
Preference pref = entries.getPreference(i); for (ScreenLockType lock : ScreenLockType.values()) {
String key = lock.preferenceKey;
Preference pref = findPreference(key);
if (pref instanceof RestrictedPreference) { if (pref instanceof RestrictedPreference) {
final String key = pref.getKey(); boolean visible = mController.isScreenLockVisible(lock);
boolean enabled = true; boolean enabled = mController.isScreenLockEnabled(lock, quality);
boolean visible = true; boolean disabledByAdmin =
boolean disabledByAdmin = false; mController.isScreenLockDisabledByAdmin(lock, adminEnforcedQuality);
if (KEY_UNLOCK_SET_OFF.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
if (getResources().getBoolean(R.bool.config_hide_none_security_option)) {
enabled = false;
visible = false;
}
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
if (getResources().getBoolean(R.bool.config_hide_swipe_security_option)) {
enabled = false;
visible = false;
} else {
if (mUserId != UserHandle.myUserId()) {
// Swipe doesn't make sense for profiles.
visible = false;
}
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
} else if (KEY_UNLOCK_SET_MANAGED.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_MANAGED
&& mManagedPasswordProvider.isManagedPasswordChoosable();
disabledByAdmin = adminEnforcedQuality
> DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
}
if (hideDisabled) { if (hideDisabled) {
visible = enabled; visible = visible && enabled;
} }
if (!visible) { if (!visible) {
entries.removePreference(pref); entries.removePreference(pref);
@@ -583,21 +560,10 @@ public class ChooseLockGeneric extends SettingsActivity {
return; return;
} }
CharSequence summary = getString(R.string.secure_lock_encryption_warning); setPreferenceSummary(ScreenLockType.PATTERN, R.string.secure_lock_encryption_warning);
setPreferenceSummary(ScreenLockType.PIN, R.string.secure_lock_encryption_warning);
PreferenceScreen screen = getPreferenceScreen(); setPreferenceSummary(ScreenLockType.PASSWORD, R.string.secure_lock_encryption_warning);
final int preferenceCount = screen.getPreferenceCount(); setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning);
for (int i = 0; i < preferenceCount; i++) {
Preference preference = screen.getPreference(i);
switch (preference.getKey()) {
case KEY_UNLOCK_SET_PATTERN:
case KEY_UNLOCK_SET_PIN:
case KEY_UNLOCK_SET_PASSWORD:
case KEY_UNLOCK_SET_MANAGED: {
preference.setSummary(summary);
} break;
}
}
} }
protected Intent getLockManagedPasswordIntent(String password) { protected Intent getLockManagedPasswordIntent(String password) {
@@ -643,17 +609,24 @@ public class ChooseLockGeneric extends SettingsActivity {
* *
* @param quality the desired quality. Ignored if DevicePolicyManager requires more security * @param quality the desired quality. Ignored if DevicePolicyManager requires more security
* @param disabled whether or not to show LockScreen at all. Only meaningful when quality is * @param disabled whether or not to show LockScreen at all. Only meaningful when quality is
* @param chooseLockSkipped whether or not this activity is skipped. This is true when this
* activity was not shown to the user at all, instead automatically proceeding based on
* the given intent extras, typically {@link LockPatternUtils#PASSWORD_TYPE_KEY}.
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED} * {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
*/ */
void updateUnlockMethodAndFinish(int quality, boolean disabled) { void updateUnlockMethodAndFinish(int quality, boolean disabled, boolean chooseLockSkipped) {
// Sanity check. We should never get here without confirming user's existing password. // Sanity check. We should never get here without confirming user's existing password.
if (!mPasswordConfirmed) { if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming it"); throw new IllegalStateException("Tried to update password without confirming it");
} }
quality = upgradeQuality(quality); quality = mController.upgradeQuality(quality);
Intent intent = getIntentForUnlockMethod(quality); Intent intent = getIntentForUnlockMethod(quality);
if (intent != null) { if (intent != null) {
if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) {
intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped);
}
intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras());
startActivityForResult(intent, startActivityForResult(intent,
mIsSetNewPassword && mHasChallenge mIsSetNewPassword && mHasChallenge
? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST ? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
@@ -831,35 +804,33 @@ public class ChooseLockGeneric extends SettingsActivity {
} }
private boolean isUnlockMethodSecure(String unlockMethod) { private boolean isUnlockMethodSecure(String unlockMethod) {
return !(KEY_UNLOCK_SET_OFF.equals(unlockMethod) || return !(ScreenLockType.SWIPE.preferenceKey.equals(unlockMethod) ||
KEY_UNLOCK_SET_NONE.equals(unlockMethod)); ScreenLockType.NONE.preferenceKey.equals(unlockMethod));
} }
private boolean setUnlockMethod(String unlockMethod) { private boolean setUnlockMethod(String unlockMethod) {
EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod); EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
if (KEY_UNLOCK_SET_OFF.equals(unlockMethod)) { ScreenLockType lock = ScreenLockType.fromKey(unlockMethod);
updateUnlockMethodAndFinish( if (lock != null) {
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true /* disabled */ ); switch (lock) {
} else if (KEY_UNLOCK_SET_NONE.equals(unlockMethod)) { case NONE:
updateUnlockMethodAndFinish( case SWIPE:
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false /* disabled */ ); updateUnlockMethodAndFinish(
} else if (KEY_UNLOCK_SET_MANAGED.equals(unlockMethod)) { lock.defaultQuality,
maybeEnableEncryption(DevicePolicyManager.PASSWORD_QUALITY_MANAGED, false); lock == ScreenLockType.NONE,
} else if (KEY_UNLOCK_SET_PATTERN.equals(unlockMethod)) { false /* chooseLockSkipped */);
maybeEnableEncryption( return true;
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false); case PATTERN:
} else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) { case PIN:
maybeEnableEncryption( case PASSWORD:
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false); case MANAGED:
} else if (KEY_UNLOCK_SET_PASSWORD.equals(unlockMethod)) { maybeEnableEncryption(lock.defaultQuality, false);
maybeEnableEncryption( return true;
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false); }
} else {
Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
return false;
} }
return true; Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
return false;
} }
private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) { private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
@@ -905,23 +876,12 @@ public class ChooseLockGeneric extends SettingsActivity {
.setTitle(args.getInt(ARG_TITLE_RES)) .setTitle(args.getInt(ARG_TITLE_RES))
.setMessage(args.getInt(ARG_MESSAGE_RES)) .setMessage(args.getInt(ARG_MESSAGE_RES))
.setPositiveButton(R.string.unlock_disable_frp_warning_ok, .setPositiveButton(R.string.unlock_disable_frp_warning_ok,
new DialogInterface.OnClickListener() { (dialog, whichButton) -> {
@Override String unlockMethod = args.getString(ARG_UNLOCK_METHOD_TO_SET);
public void onClick(DialogInterface dialog, int whichButton) { ((ChooseLockGenericFragment) getParentFragment())
((ChooseLockGenericFragment) getParentFragment()) .setUnlockMethod(unlockMethod);
.setUnlockMethod( })
args.getString(ARG_UNLOCK_METHOD_TO_SET)); .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dismiss())
}
}
)
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dismiss();
}
}
)
.create(); .create();
} }

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2017 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.password;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
/**
* A controller for ChooseLockGeneric, and other similar classes which shows a list of possible
* screen locks for the user to choose from.
*/
public class ChooseLockGenericController {
private final Context mContext;
private final int mUserId;
private ManagedLockPasswordProvider mManagedPasswordProvider;
private DevicePolicyManager mDpm;
public ChooseLockGenericController(Context context, int userId) {
this(
context,
userId,
context.getSystemService(DevicePolicyManager.class),
ManagedLockPasswordProvider.get(context, userId));
}
@VisibleForTesting
ChooseLockGenericController(
Context context,
int userId,
DevicePolicyManager dpm,
ManagedLockPasswordProvider managedLockPasswordProvider) {
mContext = context;
mUserId = userId;
mManagedPasswordProvider = managedLockPasswordProvider;
mDpm = dpm;
}
/**
* @return The higher quality of either the specified {@code quality} or the quality required
* by {@link DevicePolicyManager#getPasswordQuality}.
*/
public int upgradeQuality(int quality) {
// Compare min allowed password quality
return Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
}
/**
* Whether the given screen lock type should be visible in the given context.
*/
public boolean isScreenLockVisible(ScreenLockType type) {
switch (type) {
case NONE:
return !mContext.getResources().getBoolean(R.bool.config_hide_none_security_option);
case SWIPE:
return !mContext.getResources().getBoolean(R.bool.config_hide_swipe_security_option)
// Swipe doesn't make sense for profiles.
&& mUserId == UserHandle.myUserId();
case MANAGED:
return mManagedPasswordProvider.isManagedPasswordChoosable();
}
return true;
}
/**
* Whether screen lock with {@code type} should be enabled.
*
* @param type The screen lock type.
* @param quality The minimum required quality. This can either be requirement by device policy
* manager or because some flow only makes sense with secure lock screens.
*/
public boolean isScreenLockEnabled(ScreenLockType type, int quality) {
return type.maxQuality >= quality;
}
/**
* Whether screen lock with {@code type} is disabled by device policy admin.
*
* @param type The screen lock type.
* @param adminEnforcedQuality The minimum quality that the admin enforces.
*/
public boolean isScreenLockDisabledByAdmin(ScreenLockType type, int adminEnforcedQuality) {
boolean disabledByAdmin = type.maxQuality < adminEnforcedQuality;
if (type == ScreenLockType.MANAGED) {
disabledByAdmin = disabledByAdmin
|| !mManagedPasswordProvider.isManagedPasswordChoosable();
}
return disabledByAdmin;
}
/**
* User friendly title for the given screen lock type.
*/
public CharSequence getTitle(ScreenLockType type) {
switch (type) {
case NONE:
return mContext.getText(R.string.unlock_set_unlock_off_title);
case SWIPE:
return mContext.getText(R.string.unlock_set_unlock_none_title);
case PATTERN:
return mContext.getText(R.string.unlock_set_unlock_pattern_title);
case PIN:
return mContext.getText(R.string.unlock_set_unlock_pin_title);
case PASSWORD:
return mContext.getText(R.string.unlock_set_unlock_password_title);
case MANAGED:
return mManagedPasswordProvider.getPickerOptionTitle(false);
}
return null;
}
/**
* Gets a list of screen locks that should be visible for the given quality. The returned list
* is ordered in the natural order of the enum (the order those enums were defined).
*
* @param quality The minimum quality required in the context of the current flow. This should
* be one of the constants defined in
* {@code DevicePolicyManager#PASSWORD_QUALITY_*}.
* @param includeDisabled Whether to include screen locks disabled by {@code quality}
* requirements in the returned list.
*/
@NonNull
public List<ScreenLockType> getVisibleScreenLockTypes(int quality, boolean includeDisabled) {
int upgradedQuality = upgradeQuality(quality);
List<ScreenLockType> locks = new ArrayList<>();
// EnumSet's iterator guarantees the natural order of the enums
for (ScreenLockType lock : ScreenLockType.values()) {
if (isScreenLockVisible(lock)) {
if (includeDisabled || isScreenLockEnabled(lock, upgradedQuality)) {
locks.add(lock);
}
}
}
return locks;
}
}

View File

@@ -28,6 +28,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics; import android.app.admin.PasswordMetrics;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Insets;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
@@ -171,7 +172,7 @@ public class ChooseLockPassword extends SettingsActivity {
private int mPasswordMinNumeric = 0; private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0; private int mPasswordMinNonLetter = 0;
private int mPasswordMinLengthToFulfillAllPolicies = 0; private int mPasswordMinLengthToFulfillAllPolicies = 0;
private int mUserId; protected int mUserId;
private boolean mHideDrawer = false; private boolean mHideDrawer = false;
/** /**
* Password requirements that we need to verify. * Password requirements that we need to verify.
@@ -188,7 +189,7 @@ public class ChooseLockPassword extends SettingsActivity {
private TextView mHeaderText; private TextView mHeaderText;
private String mFirstPin; private String mFirstPin;
private RecyclerView mPasswordRestrictionView; private RecyclerView mPasswordRestrictionView;
private boolean mIsAlphaMode; protected boolean mIsAlphaMode;
private Button mCancelButton; private Button mCancelButton;
private Button mNextButton; private Button mNextButton;
@@ -291,6 +292,11 @@ public class ChooseLockPassword extends SettingsActivity {
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
// Make the password container consume the optical insets so the edit text is aligned
// with the sides of the parent visually.
ViewGroup container = view.findViewById(R.id.password_container);
container.setOpticalInsets(Insets.NONE);
mCancelButton = (Button) view.findViewById(R.id.cancel_button); mCancelButton = (Button) view.findViewById(R.id.cancel_button);
mCancelButton.setOnClickListener(this); mCancelButton.setOnClickListener(this);
mNextButton = (Button) view.findViewById(R.id.next_button); mNextButton = (Button) view.findViewById(R.id.next_button);

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2017 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.password;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import java.util.List;
/**
* A dialog fragment similar to {@link ChooseLockGeneric} where the user can select from a few
* lock screen types.
*/
public class ChooseLockTypeDialogFragment extends InstrumentedDialogFragment
implements OnClickListener {
private static final String ARG_USER_ID = "userId";
private static final String ARG_EXCLUDE_LOCK = "excludeLock";
private ScreenLockAdapter mAdapter;
private ChooseLockGenericController mController;
public static ChooseLockTypeDialogFragment newInstance(int userId, String excludeLock) {
Bundle args = new Bundle();
args.putInt(ARG_USER_ID, userId);
args.putString(ARG_EXCLUDE_LOCK, excludeLock);
ChooseLockTypeDialogFragment fragment = new ChooseLockTypeDialogFragment();
fragment.setArguments(args);
return fragment;
}
public interface OnLockTypeSelectedListener {
void onLockTypeSelected(ScreenLockType lock);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int userId = getArguments().getInt(ARG_USER_ID);
mController = new ChooseLockGenericController(getContext(), userId);
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
OnLockTypeSelectedListener listener = null;
Fragment parentFragment = getParentFragment();
if (parentFragment instanceof OnLockTypeSelectedListener) {
listener = (OnLockTypeSelectedListener) parentFragment;
} else {
Context context = getContext();
if (context instanceof OnLockTypeSelectedListener) {
listener = (OnLockTypeSelectedListener) context;
}
}
if (listener != null) {
listener.onLockTypeSelected(mAdapter.getItem(i));
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Context context = getContext();
Builder builder = new Builder(context);
List<ScreenLockType> locks =
mController.getVisibleScreenLockTypes(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
false /* includeDisabled */);
String excludeLockName = getArguments().getString(ARG_EXCLUDE_LOCK);
if (excludeLockName != null) {
locks.remove(ScreenLockType.valueOf(excludeLockName));
}
mAdapter = new ScreenLockAdapter(context, locks, mController);
builder.setAdapter(mAdapter, this);
return builder.create();
}
@Override
public int getMetricsCategory() {
return MetricsEvent.SETTINGS_CHOOSE_LOCK_DIALOG;
}
private static class ScreenLockAdapter extends ArrayAdapter<ScreenLockType> {
private final ChooseLockGenericController mController;
ScreenLockAdapter(
Context context,
List<ScreenLockType> locks,
ChooseLockGenericController controller) {
super(context, android.R.layout.simple_list_item_1, locks);
mController = controller;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_1, parent, false);
}
((TextView) view).setText(mController.getTitle(getItem(position)));
return view;
}
}
}

View File

@@ -52,7 +52,7 @@ public class ManagedLockPasswordProvider {
* Should be overridden if {@link #isManagedPasswordSupported()} returns true. * Should be overridden if {@link #isManagedPasswordSupported()} returns true.
* @param forFingerprint Whether fingerprint unlock is enabled. * @param forFingerprint Whether fingerprint unlock is enabled.
*/ */
String getPickerOptionTitle(boolean forFingerprint) { return ""; } CharSequence getPickerOptionTitle(boolean forFingerprint) { return ""; }
/** /**
* Gets resource id of the lock screen preference that should be displayed in security settings * Gets resource id of the lock screen preference that should be displayed in security settings

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2017 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.password;
import android.app.admin.DevicePolicyManager;
/**
* List of screen lock type options that are available in ChooseLockGeneric. Provides the key and
* the associated quality, and also some helper functions to translate between them.
*/
public enum ScreenLockType {
NONE(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
"unlock_set_off"),
SWIPE(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
"unlock_set_none"),
PATTERN(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
"unlock_set_pattern"),
PIN(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
"unlock_set_pin"),
PASSWORD(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
DevicePolicyManager.PASSWORD_QUALITY_COMPLEX,
"unlock_set_password"),
MANAGED(
DevicePolicyManager.PASSWORD_QUALITY_MANAGED,
"unlock_set_managed");
private static final ScreenLockType MIN_QUALITY = ScreenLockType.NONE;
private static final ScreenLockType MAX_QUALITY = ScreenLockType.MANAGED;
/**
* The default quality of the type of lock used. For example, in the case of PIN, the default
* quality if PASSWORD_QUALITY_NUMERIC, while the highest quality is
* PASSWORD_QUALITY_NUMERIC_COMPLEX.
*/
public final int defaultQuality;
/**
* The highest quality for the given type of lock. For example, in the case of password, the
* default quality is PASSWORD_QUALITY_ALPHABETIC, but the highest possible quality is
* PASSWORD_QUALITY_COMPLEX.
*/
public final int maxQuality;
public final String preferenceKey;
ScreenLockType(int quality, String preferenceKey) {
this(quality, quality, preferenceKey);
}
ScreenLockType(int defaultQuality, int maxQuality, String preferenceKey) {
this.defaultQuality = defaultQuality;
this.maxQuality = maxQuality;
this.preferenceKey = preferenceKey;
}
/**
* Gets the screen lock type for the given quality. Note that this method assumes that a screen
* lock is enabled, which means if the quality is
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, the returned type will be
* {@link #SWIPE} and not {@link #NONE}.
*/
public static ScreenLockType fromQuality(int quality) {
switch (quality) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
return ScreenLockType.PATTERN;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return ScreenLockType.PIN;
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
return ScreenLockType.PASSWORD;
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
return ScreenLockType.MANAGED;
case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
return ScreenLockType.SWIPE;
}
return null;
}
public static ScreenLockType fromKey(String key) {
for (ScreenLockType lock : ScreenLockType.values()) {
if (lock.preferenceKey.equals(key)) {
return lock;
}
}
return null;
}
}

View File

@@ -111,20 +111,16 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_CANCELED) { if (data == null) {
if (data == null) { data = new Intent();
data = new Intent();
}
// Add the password quality extra to the intent data that will be sent back for
// Setup Wizard.
LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
data.putExtra(EXTRA_PASSWORD_QUALITY,
lockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId()));
super.onActivityResult(requestCode, resultCode, data);
} }
// If the started activity was cancelled (e.g. the user presses back), then this // Add the password quality extra to the intent data that will be sent back for
// activity will be resumed to foreground. // Setup Wizard.
LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
data.putExtra(EXTRA_PASSWORD_QUALITY,
lockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId()));
super.onActivityResult(requestCode, resultCode, data);
} }
@Override @Override

View File

@@ -16,16 +16,24 @@
package com.android.settings.password; package com.android.settings.password;
import android.app.Activity;
import android.app.Fragment; import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SetupRedactionInterstitial; import com.android.settings.SetupRedactionInterstitial;
import com.android.settings.SetupWizardUtils; import com.android.settings.SetupWizardUtils;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.password.ChooseLockTypeDialogFragment.OnLockTypeSelectedListener;
import com.android.setupwizardlib.util.WizardManagerHelper;
/** /**
* Setup Wizard's version of ChooseLockPassword screen. It inherits the logic and basic structure * Setup Wizard's version of ChooseLockPassword screen. It inherits the logic and basic structure
@@ -67,7 +75,41 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
super.onApplyThemeResource(theme, resid, first); super.onApplyThemeResource(theme, resid, first);
} }
public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment { public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment
implements OnLockTypeSelectedListener {
@VisibleForTesting
static final int REQUEST_SCREEN_LOCK_OPTIONS = 1;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
boolean showOptionsButton = getActivity().getIntent().getBooleanExtra(
ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, false);
if (showOptionsButton) {
Button optionsButton = view.findViewById(R.id.screen_lock_options);
optionsButton.setVisibility(View.VISIBLE);
optionsButton.setOnClickListener(this);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.screen_lock_options:
launchChooseLockGeneric();
break;
default:
super.onClick(v);
}
}
private void launchChooseLockGeneric() {
ScreenLockType currentLock = mIsAlphaMode
? ScreenLockType.PASSWORD : ScreenLockType.PIN;
ChooseLockTypeDialogFragment.newInstance(mUserId, currentLock.toString())
.show(getChildFragmentManager(), null);
}
@Override @Override
protected Intent getRedactionInterstitialIntent(Context context) { protected Intent getRedactionInterstitialIntent(Context context) {
@@ -76,5 +118,46 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
SetupRedactionInterstitial.setEnabled(context, true); SetupRedactionInterstitial.setEnabled(context, true);
return null; return null;
} }
@Override
public void onLockTypeSelected(ScreenLockType lock) {
Intent activityIntent = getActivity().getIntent();
Intent intent = new Intent(getContext(), SetupChooseLockGeneric.class);
// Copy the original extras into the new intent
if (activityIntent
.hasExtra(ChooseLockGenericFragment.EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS)) {
intent.putExtras(activityIntent.getBundleExtra(
ChooseLockGenericFragment.EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS));
}
intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, lock.defaultQuality);
// Propagate the fingerprint challenge
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE,
activityIntent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE,
false));
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE,
activityIntent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0));
// The user is already given the choice of the what screen lock to set up. No need to
// show this button again.
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, false);
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
startActivityForResult(intent, REQUEST_SCREEN_LOCK_OPTIONS);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SCREEN_LOCK_OPTIONS) {
if (resultCode != Activity.RESULT_CANCELED) {
Activity activity = getActivity();
activity.setResult(resultCode, data);
activity.finish();
}
}
}
} }
} }

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2017 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 android.app.admin;
import android.annotation.NonNull;
/**
* Stub implementation of framework's PasswordMetrics for Robolectric tests. Otherwise Robolectric
* is throwing ClassNotFoundError.
*
* TODO: Remove this class when Robolectric supports O
*/
public class PasswordMetrics {
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
public int length = 0;
public int letters = 0;
public int upperCase = 0;
public int lowerCase = 0;
public int numeric = 0;
public int symbols = 0;
public int nonLetter = 0;
public static int maxLengthSequence(@NonNull String string) {
// Stub implementation
return 1;
}
public static PasswordMetrics computeForPassword(@NonNull String password) {
return new PasswordMetrics();
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2017 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.password;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.regex.Pattern;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class
})
public class ChooseLockGenericControllerTest {
private ChooseLockGenericController mController;
@Mock
private ManagedLockPasswordProvider mManagedLockPasswordProvider;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new ChooseLockGenericController(
application,
0 /* userId */,
mDevicePolicyManager,
mManagedLockPasswordProvider);
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false);
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false);
}
@After
public void tearDown() {
SettingsShadowResources.reset();
}
@Test
public void isScreenLockVisible_shouldRespectResourceConfig() {
for (ScreenLockType lock : ScreenLockType.values()) {
// All locks except managed defaults to visible
assertThat(mController.isScreenLockVisible(lock)).named(lock + " visible")
.isEqualTo(lock != ScreenLockType.MANAGED);
}
SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, true);
SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, true);
assertThat(mController.isScreenLockVisible(ScreenLockType.NONE)).named("NONE visible")
.isFalse();
assertThat(mController.isScreenLockVisible(ScreenLockType.SWIPE)).named("SWIPE visible")
.isFalse();
}
@Test
public void isScreenLockVisible_notCurrentUser_shouldHideSwipe() {
mController = new ChooseLockGenericController(application, 1 /* userId */);
assertThat(mController.isScreenLockVisible(ScreenLockType.SWIPE)).named("SWIPE visible")
.isFalse();
}
@Test
public void isScreenLockVisible_managedPasswordChoosable_shouldShowManaged() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
assertThat(mController.isScreenLockVisible(ScreenLockType.MANAGED)).named("MANAGED visible")
.isTrue();
}
@Test
public void isScreenLockEnabled_lowerQuality_shouldReturnFalse() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.maxQuality + 1))
.named(lock + " enabled")
.isFalse();
}
}
@Test
public void isScreenLockEnabled_equalQuality_shouldReturnTrue() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.defaultQuality))
.named(lock + " enabled")
.isTrue();
}
}
@Test
public void isScreenLockEnabled_higherQuality_shouldReturnTrue() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockEnabled(lock, lock.maxQuality - 1))
.named(lock + " enabled")
.isTrue();
}
}
@Test
public void isScreenLockDisabledByAdmin_lowerQuality_shouldReturnTrue() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality + 1))
.named(lock + " disabledByAdmin")
.isTrue();
}
}
@Test
public void isScreenLockDisabledByAdmin_equalQuality_shouldReturnFalse() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality))
.named(lock + " disabledByAdmin")
.isFalse();
}
}
@Test
public void isScreenLockDisabledByAdmin_higherQuality_shouldReturnFalse() {
doReturn(true).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.isScreenLockDisabledByAdmin(lock, lock.maxQuality - 1))
.named(lock + " disabledByAdmin")
.isFalse();
}
}
@Test
public void isScreenLockDisabledByAdmin_managedNotChoosable_shouldReturnTrue() {
doReturn(false).when(mManagedLockPasswordProvider).isManagedPasswordChoosable();
assertThat(mController.isScreenLockDisabledByAdmin(
ScreenLockType.MANAGED, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
.named("MANANGED disabledByAdmin")
.isTrue();
}
@Test
public void getTitle_shouldContainEnumName() {
doReturn("MANAGED").when(mManagedLockPasswordProvider).getPickerOptionTitle(anyBoolean());
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(mController.getTitle(lock).toString())
.containsMatch(Pattern.compile(lock.toString(), Pattern.CASE_INSENSITIVE));
}
}
@Test
public void getVisibleScreenLockTypes_qualitySomething_shouldReturnPatterPinPassword() {
assertThat(mController.getVisibleScreenLockTypes(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false))
.isEqualTo(Arrays.asList(
ScreenLockType.PATTERN,
ScreenLockType.PIN,
ScreenLockType.PASSWORD));
}
@Test
public void getVisibleScreenLockTypes_showDisabled_shouldReturnAllButManaged() {
assertThat(mController.getVisibleScreenLockTypes(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, true))
.isEqualTo(Arrays.asList(
ScreenLockType.NONE,
ScreenLockType.SWIPE,
ScreenLockType.PATTERN,
ScreenLockType.PIN,
ScreenLockType.PASSWORD));
}
@Test
public void upgradeQuality_noDpmRequirement_shouldReturnQuality() {
doReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED)
.when(mDevicePolicyManager).getPasswordQuality(any(ComponentName.class), anyInt());
int upgradedQuality = mController.upgradeQuality(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertThat(upgradedQuality).named("upgradedQuality")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
}
@Test
public void upgradeQuality_dpmRequirement_shouldReturnRequiredQuality() {
doReturn(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC)
.when(mDevicePolicyManager).getPasswordQuality(any(ComponentName.class), anyInt());
int upgradedQuality = mController.upgradeQuality(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
assertThat(upgradedQuality).named("upgradedQuality")
.isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2017 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.password;
import static com.google.common.truth.Truth.assertThat;
import android.app.admin.DevicePolicyManager;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION)
public class ScreenLockTypeTest {
@Test
public void fromQuality_shouldReturnLockWithAssociatedQuality() {
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK))
.isNull();
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX))
.isEqualTo(ScreenLockType.PASSWORD);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_MANAGED))
.isEqualTo(ScreenLockType.MANAGED);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC))
.isEqualTo(ScreenLockType.PIN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX))
.isEqualTo(ScreenLockType.PIN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING))
.isEqualTo(ScreenLockType.PATTERN);
assertThat(ScreenLockType.fromQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
.isEqualTo(ScreenLockType.SWIPE);
}
@Test
public void fromKey_shouldReturnLockWithGivenKey() {
for (ScreenLockType lock : ScreenLockType.values()) {
assertThat(ScreenLockType.fromKey(lock.preferenceKey)).isEqualTo(lock);
}
assertThat(ScreenLockType.fromKey("nonexistent")).isNull();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2017 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.password;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
import com.android.settings.password.SetupChooseLockPassword.SetupChooseLockPasswordFragment;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.shadows.ShadowActivity.IntentForResult;
import org.robolectric.shadows.ShadowDialog;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
ShadowDynamicIndexableContentMonitor.class,
ShadowEventLogWriter.class,
ShadowUtils.class
})
public class SetupChooseLockPasswordTest {
@Test
public void createActivity_shouldNotCrash() {
// Basic sanity test for activity created without crashing
Robolectric.buildActivity(SetupChooseLockPassword.class,
SetupChooseLockPassword.modifyIntentForSetup(
application,
new IntentBuilder(application).build()))
.setup().get();
}
@Test
public void createActivity_withShowOptionsButtonExtra_shouldShowButton() {
Intent intent = SetupChooseLockPassword.modifyIntentForSetup(
application,
new IntentBuilder(application).build());
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
SetupChooseLockPassword activity =
Robolectric.buildActivity(SetupChooseLockPassword.class, intent).setup().get();
Button optionsButton = activity.findViewById(R.id.screen_lock_options);
assertThat(optionsButton).isNotNull();
ShadowActivity shadowActivity = shadowOf(activity);
optionsButton.performClick();
assertThat(ShadowDialog.getLatestDialog()).isNotNull();
}
@Test
public void createActivity_clickDifferentOption_extrasShouldBePropagated() {
Bundle bundle = new Bundle();
bundle.putString("foo", "bar");
Intent intent = new IntentBuilder(application).build();
intent.putExtra(ChooseLockGenericFragment.EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, bundle);
intent = SetupChooseLockPassword.modifyIntentForSetup(application, intent);
intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
SetupChooseLockPassword activity =
Robolectric.buildActivity(SetupChooseLockPassword.class, intent).setup().get();
SetupChooseLockPasswordFragment fragment =
(SetupChooseLockPasswordFragment) activity.getFragmentManager()
.findFragmentById(R.id.main_content);
fragment.onLockTypeSelected(ScreenLockType.PATTERN);
ShadowActivity shadowActivity = shadowOf(activity);
IntentForResult chooseLockIntent = shadowActivity.getNextStartedActivityForResult();
assertThat(chooseLockIntent).isNotNull();
assertThat(chooseLockIntent.requestCode)
.isEqualTo(SetupChooseLockPasswordFragment.REQUEST_SCREEN_LOCK_OPTIONS);
assertThat(chooseLockIntent.intent.getStringExtra("foo")).named("Foo extra")
.isEqualTo("bar");
}
}

View File

@@ -137,6 +137,16 @@ public class SettingsShadowResources extends ShadowResources {
realResources, Resources.class, "getInteger", ClassParameter.from(int.class, id)); realResources, Resources.class, "getInteger", ClassParameter.from(int.class, id));
} }
@Implementation
public boolean getBoolean(int id) {
final Object override = sResourceOverrides.get(id);
if (override instanceof Boolean) {
return (boolean) override;
}
return Shadow.directlyOn(realResources, Resources.class, "getBoolean",
ClassParameter.from(int.class, id));
}
@Implements(Theme.class) @Implements(Theme.class)
public static class SettingsShadowTheme extends ShadowTheme { public static class SettingsShadowTheme extends ShadowTheme {