diff --git a/res/layout/choose_lock_password.xml b/res/layout/choose_lock_password.xml
index b0ba043610c..988f331940a 100644
--- a/res/layout/choose_lock_password.xml
+++ b/res/layout/choose_lock_password.xml
@@ -43,11 +43,13 @@
android:textAppearance="?android:attr/textAppearanceMedium"/>
@@ -68,6 +70,15 @@
android:layout_height="wrap_content"/>
+
+
+
diff --git a/res/layout/password_requirement_item.xml b/res/layout/password_requirement_item.xml
index df7f45c941a..57cbdc704eb 100644
--- a/res/layout/password_requirement_item.xml
+++ b/res/layout/password_requirement_item.xml
@@ -18,5 +18,4 @@
android:id="@+id/description_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:textSize="14sp"/>
\ No newline at end of file
+ android:textSize="14sp"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 952ac438915..45b0abbf0a5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1088,6 +1088,9 @@
Choose your backup screen lock method
+
+ Screen lock options
+
Screen lock
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
index a1f4017fbc7..26b3427396d 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -17,6 +17,7 @@
package com.android.settings.fingerprint;
import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.content.res.Resources;
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.settings.R;
import com.android.settings.SetupWizardUtils;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.password.SetupChooseLockGeneric;
public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
@Override
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);
return intent;
}
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index bbf5a1614f9..5e2d8953a95 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -29,7 +29,6 @@ import android.app.Fragment;
import android.app.FragmentManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.hardware.fingerprint.Fingerprint;
@@ -40,6 +39,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.security.KeyStore;
+import android.support.annotation.StringRes;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
@@ -95,14 +95,9 @@ public class ChooseLockGeneric extends SettingsActivity {
}
public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
+
private static final String TAG = "ChooseLockGenericFragment";
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 PASSWORD_CONFIRMED = "password_confirmed";
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 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 ENABLE_ENCRYPTION_REQUEST = 101;
private static final int CHOOSE_LOCK_REQUEST = 102;
@@ -136,6 +152,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private ManagedLockPasswordProvider mManagedPasswordProvider;
private boolean mIsSetNewPassword = false;
private UserManager mUserManager;
+ private ChooseLockGenericController mController;
protected boolean mForFingerprint = false;
@@ -192,6 +209,7 @@ public class ChooseLockGeneric extends SettingsActivity {
UserManager.get(getActivity()),
getArguments(),
getActivity().getIntent().getExtras()).getIdentifier();
+ mController = new ChooseLockGenericController(getContext(), mUserId);
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -303,7 +321,7 @@ public class ChooseLockGeneric extends SettingsActivity {
finish();
return;
}
- updateUnlockMethodAndFinish(quality, disabled);
+ updateUnlockMethodAndFinish(quality, disabled, false /* chooseLockSkipped */);
}
}
@@ -328,6 +346,15 @@ public class ChooseLockGeneric extends SettingsActivity {
if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
getActivity().setResult(resultCode, data);
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
&& resultCode == FingerprintEnrollBase.RESULT_FINISHED) {
@@ -374,7 +401,7 @@ public class ChooseLockGeneric extends SettingsActivity {
if (quality == -1) {
// If caller didn't specify password quality, show UI and allow the user to choose.
quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
- quality = upgradeQuality(quality);
+ quality = mController.upgradeQuality(quality);
final boolean hideDisabledPrefs = intent.getBooleanExtra(
HIDE_DISABLED_PREFS, false);
final PreferenceScreen prefScreen = getPreferenceScreen();
@@ -387,7 +414,7 @@ public class ChooseLockGeneric extends SettingsActivity {
updateCurrentPreference();
updatePreferenceSummaryIfNeeded();
} else {
- updateUnlockMethodAndFinish(quality, false);
+ updateUnlockMethodAndFinish(quality, false, true /* chooseLockSkipped */);
}
}
@@ -395,33 +422,26 @@ public class ChooseLockGeneric extends SettingsActivity {
addPreferencesFromResource(R.xml.security_settings_picker);
// 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_UNLOCK_SET_PIN).setViewId(R.id.lock_pin);
- findPreference(KEY_UNLOCK_SET_PASSWORD).setViewId(R.id.lock_password);
+ findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
+ findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
}
private void updatePreferenceText() {
if (mForFingerprint) {
- final String key[] = { KEY_UNLOCK_SET_PATTERN,
- KEY_UNLOCK_SET_PIN,
- KEY_UNLOCK_SET_PASSWORD };
- final int res[] = { R.string.fingerprint_unlock_set_unlock_pattern,
- R.string.fingerprint_unlock_set_unlock_pin,
- 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]);
- }
- }
+ setPreferenceTitle(ScreenLockType.PATTERN,
+ R.string.fingerprint_unlock_set_unlock_pattern);
+ setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin);
+ setPreferenceTitle(ScreenLockType.PASSWORD,
+ R.string.fingerprint_unlock_set_unlock_password);
}
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
- Preference managed = findPreference(KEY_UNLOCK_SET_MANAGED);
- managed.setTitle(mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint));
+ setPreferenceTitle(ScreenLockType.MANAGED,
+ mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint));
} else {
- removePreference(KEY_UNLOCK_SET_MANAGED);
+ removePreference(ScreenLockType.MANAGED.preferenceKey);
}
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() {
String currentKey = getKeyForCurrent();
Preference preference = findPreference(currentKey);
@@ -441,39 +482,12 @@ public class ChooseLockGeneric extends SettingsActivity {
final int credentialOwner = UserManager.get(getContext())
.getCredentialOwnerProfile(mUserId);
if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) {
- return KEY_UNLOCK_SET_OFF;
+ return ScreenLockType.NONE.preferenceKey;
}
- switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner)) {
- case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
- return KEY_UNLOCK_SET_PATTERN;
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- 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;
+ ScreenLockType lock =
+ ScreenLockType.fromQuality(
+ mLockPatternUtils.getKeyguardStoredPasswordQuality(credentialOwner));
+ return lock != null ? lock.preferenceKey : null;
}
/***
@@ -501,54 +515,17 @@ public class ChooseLockGeneric extends SettingsActivity {
int adminEnforcedQuality = mDPM.getPasswordQuality(null, mUserId);
EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfPasswordQualityIsSet(
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) {
- final String key = pref.getKey();
- boolean enabled = true;
- boolean visible = true;
- boolean disabledByAdmin = false;
- 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;
- }
+ boolean visible = mController.isScreenLockVisible(lock);
+ boolean enabled = mController.isScreenLockEnabled(lock, quality);
+ boolean disabledByAdmin =
+ mController.isScreenLockDisabledByAdmin(lock, adminEnforcedQuality);
if (hideDisabled) {
- visible = enabled;
+ visible = visible && enabled;
}
if (!visible) {
entries.removePreference(pref);
@@ -583,21 +560,10 @@ public class ChooseLockGeneric extends SettingsActivity {
return;
}
- CharSequence summary = getString(R.string.secure_lock_encryption_warning);
-
- PreferenceScreen screen = getPreferenceScreen();
- final int preferenceCount = screen.getPreferenceCount();
- 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;
- }
- }
+ setPreferenceSummary(ScreenLockType.PATTERN, R.string.secure_lock_encryption_warning);
+ setPreferenceSummary(ScreenLockType.PIN, R.string.secure_lock_encryption_warning);
+ setPreferenceSummary(ScreenLockType.PASSWORD, R.string.secure_lock_encryption_warning);
+ setPreferenceSummary(ScreenLockType.MANAGED, R.string.secure_lock_encryption_warning);
}
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 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}
*/
- 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.
if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming it");
}
- quality = upgradeQuality(quality);
+ quality = mController.upgradeQuality(quality);
Intent intent = getIntentForUnlockMethod(quality);
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,
mIsSetNewPassword && mHasChallenge
? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
@@ -831,35 +804,33 @@ public class ChooseLockGeneric extends SettingsActivity {
}
private boolean isUnlockMethodSecure(String unlockMethod) {
- return !(KEY_UNLOCK_SET_OFF.equals(unlockMethod) ||
- KEY_UNLOCK_SET_NONE.equals(unlockMethod));
+ return !(ScreenLockType.SWIPE.preferenceKey.equals(unlockMethod) ||
+ ScreenLockType.NONE.preferenceKey.equals(unlockMethod));
}
private boolean setUnlockMethod(String unlockMethod) {
EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
- if (KEY_UNLOCK_SET_OFF.equals(unlockMethod)) {
- updateUnlockMethodAndFinish(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true /* disabled */ );
- } else if (KEY_UNLOCK_SET_NONE.equals(unlockMethod)) {
- updateUnlockMethodAndFinish(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false /* disabled */ );
- } else if (KEY_UNLOCK_SET_MANAGED.equals(unlockMethod)) {
- maybeEnableEncryption(DevicePolicyManager.PASSWORD_QUALITY_MANAGED, false);
- } else if (KEY_UNLOCK_SET_PATTERN.equals(unlockMethod)) {
- maybeEnableEncryption(
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
- } else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) {
- maybeEnableEncryption(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
- } else if (KEY_UNLOCK_SET_PASSWORD.equals(unlockMethod)) {
- maybeEnableEncryption(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
- } else {
- Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
- return false;
+ ScreenLockType lock = ScreenLockType.fromKey(unlockMethod);
+ if (lock != null) {
+ switch (lock) {
+ case NONE:
+ case SWIPE:
+ updateUnlockMethodAndFinish(
+ lock.defaultQuality,
+ lock == ScreenLockType.NONE,
+ false /* chooseLockSkipped */);
+ return true;
+ case PATTERN:
+ case PIN:
+ case PASSWORD:
+ case MANAGED:
+ maybeEnableEncryption(lock.defaultQuality, false);
+ return true;
+ }
}
- return true;
+ Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
+ return false;
}
private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
@@ -905,23 +876,12 @@ public class ChooseLockGeneric extends SettingsActivity {
.setTitle(args.getInt(ARG_TITLE_RES))
.setMessage(args.getInt(ARG_MESSAGE_RES))
.setPositiveButton(R.string.unlock_disable_frp_warning_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- ((ChooseLockGenericFragment) getParentFragment())
- .setUnlockMethod(
- args.getString(ARG_UNLOCK_METHOD_TO_SET));
- }
- }
- )
- .setNegativeButton(R.string.cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- dismiss();
- }
- }
- )
+ (dialog, whichButton) -> {
+ String unlockMethod = args.getString(ARG_UNLOCK_METHOD_TO_SET);
+ ((ChooseLockGenericFragment) getParentFragment())
+ .setUnlockMethod(unlockMethod);
+ })
+ .setNegativeButton(R.string.cancel, (dialog, whichButton) -> dismiss())
.create();
}
diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java
new file mode 100644
index 00000000000..a6a1701821c
--- /dev/null
+++ b/src/com/android/settings/password/ChooseLockGenericController.java
@@ -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 getVisibleScreenLockTypes(int quality, boolean includeDisabled) {
+ int upgradedQuality = upgradeQuality(quality);
+ List 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;
+ }
+}
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index f9aabd6f414..df0621ead41 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -28,6 +28,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Insets;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -171,7 +172,7 @@ public class ChooseLockPassword extends SettingsActivity {
private int mPasswordMinNumeric = 0;
private int mPasswordMinNonLetter = 0;
private int mPasswordMinLengthToFulfillAllPolicies = 0;
- private int mUserId;
+ protected int mUserId;
private boolean mHideDrawer = false;
/**
* Password requirements that we need to verify.
@@ -188,7 +189,7 @@ public class ChooseLockPassword extends SettingsActivity {
private TextView mHeaderText;
private String mFirstPin;
private RecyclerView mPasswordRestrictionView;
- private boolean mIsAlphaMode;
+ protected boolean mIsAlphaMode;
private Button mCancelButton;
private Button mNextButton;
@@ -291,6 +292,11 @@ public class ChooseLockPassword extends SettingsActivity {
public void onViewCreated(View view, Bundle 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.setOnClickListener(this);
mNextButton = (Button) view.findViewById(R.id.next_button);
diff --git a/src/com/android/settings/password/ChooseLockTypeDialogFragment.java b/src/com/android/settings/password/ChooseLockTypeDialogFragment.java
new file mode 100644
index 00000000000..2581483df4c
--- /dev/null
+++ b/src/com/android/settings/password/ChooseLockTypeDialogFragment.java
@@ -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 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 {
+
+ private final ChooseLockGenericController mController;
+
+ ScreenLockAdapter(
+ Context context,
+ List 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;
+ }
+ }
+}
diff --git a/src/com/android/settings/password/ManagedLockPasswordProvider.java b/src/com/android/settings/password/ManagedLockPasswordProvider.java
index 65bf1f61601..09fe1049863 100644
--- a/src/com/android/settings/password/ManagedLockPasswordProvider.java
+++ b/src/com/android/settings/password/ManagedLockPasswordProvider.java
@@ -52,7 +52,7 @@ public class ManagedLockPasswordProvider {
* Should be overridden if {@link #isManagedPasswordSupported()} returns true.
* @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
diff --git a/src/com/android/settings/password/ScreenLockType.java b/src/com/android/settings/password/ScreenLockType.java
new file mode 100644
index 00000000000..608c8f6576a
--- /dev/null
+++ b/src/com/android/settings/password/ScreenLockType.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index 74aac2c0041..4e73b87d451 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -111,20 +111,16 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != RESULT_CANCELED) {
- if (data == null) {
- 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 (data == null) {
+ data = new Intent();
}
- // If the started activity was cancelled (e.g. the user presses back), then this
- // activity will be resumed to foreground.
+ // 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);
}
@Override
diff --git a/src/com/android/settings/password/SetupChooseLockPassword.java b/src/com/android/settings/password/SetupChooseLockPassword.java
index b8a741dbeba..0c62c7c1f69 100644
--- a/src/com/android/settings/password/SetupChooseLockPassword.java
+++ b/src/com/android/settings/password/SetupChooseLockPassword.java
@@ -16,16 +16,24 @@
package com.android.settings.password;
+import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
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.SetupRedactionInterstitial;
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
@@ -67,7 +75,41 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
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
protected Intent getRedactionInterstitialIntent(Context context) {
@@ -76,5 +118,46 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
SetupRedactionInterstitial.setEnabled(context, true);
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();
+ }
+ }
+ }
}
}
diff --git a/tests/robotests/src/android/app/admin/PasswordMetrics.java b/tests/robotests/src/android/app/admin/PasswordMetrics.java
new file mode 100644
index 00000000000..a46130661b0
--- /dev/null
+++ b/tests/robotests/src/android/app/admin/PasswordMetrics.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
new file mode 100644
index 00000000000..589e2df87a4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
@@ -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);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/password/ScreenLockTypeTest.java b/tests/robotests/src/com/android/settings/password/ScreenLockTypeTest.java
new file mode 100644
index 00000000000..96bce00f06a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/ScreenLockTypeTest.java
@@ -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();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java
new file mode 100644
index 00000000000..2436906d2ce
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPasswordTest.java
@@ -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");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
index cb02d3e0590..d4b0ff76b1d 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
@@ -137,6 +137,16 @@ public class SettingsShadowResources extends ShadowResources {
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)
public static class SettingsShadowTheme extends ShadowTheme {