Clean up choose lock intent creation
Consolidated the many variants of ChooseLock*.createIntent, so that it will take the same set of arguments. Also modified SetupChooseLock*.createIntent to modifyIntentForSetup, which will take the intent created by ChooseLock* and modify it for use with setup. Test: cd tests/robotests && mma Change-Id: I5ff033f459c33ec9980872a536b3996d89f2bbbb
This commit is contained in:
934
src/com/android/settings/password/ChooseLockGeneric.java
Normal file
934
src/com/android/settings/password/ChooseLockGeneric.java
Normal file
@@ -0,0 +1,934 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
|
||||
|
||||
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
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;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.security.KeyStore;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.text.TextUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.EventLogTags;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.fingerprint.FingerprintEnrollBase;
|
||||
import com.android.settings.fingerprint.FingerprintEnrollFindSensor;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChooseLockGeneric extends SettingsActivity {
|
||||
public static final String CONFIRM_CREDENTIALS = "confirm_credentials";
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
|
||||
|
||||
String action = modIntent.getAction();
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(action)
|
||||
|| ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(action)) {
|
||||
modIntent.putExtra(EXTRA_HIDE_DRAWER, true);
|
||||
}
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
if (ChooseLockGenericFragment.class.getName().equals(fragmentName)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* package */ Class<? extends Fragment> getFragmentClass() {
|
||||
return ChooseLockGenericFragment.class;
|
||||
}
|
||||
|
||||
public static class InternalActivity extends ChooseLockGeneric {
|
||||
}
|
||||
|
||||
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";
|
||||
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
|
||||
public static final String HIDE_DISABLED_PREFS = "hide_disabled_prefs";
|
||||
public static final String ENCRYPT_REQUESTED_QUALITY = "encrypt_requested_quality";
|
||||
public static final String ENCRYPT_REQUESTED_DISABLED = "encrypt_requested_disabled";
|
||||
public static final String TAG_FRP_WARNING_DIALOG = "frp_warning_dialog";
|
||||
|
||||
private static final int CONFIRM_EXISTING_REQUEST = 100;
|
||||
private static final int ENABLE_ENCRYPTION_REQUEST = 101;
|
||||
private static final int CHOOSE_LOCK_REQUEST = 102;
|
||||
private static final int CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST = 103;
|
||||
private static final int SKIP_FINGERPRINT_REQUEST = 104;
|
||||
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private DevicePolicyManager mDPM;
|
||||
private KeyStore mKeyStore;
|
||||
private boolean mHasChallenge = false;
|
||||
private long mChallenge;
|
||||
private boolean mPasswordConfirmed = false;
|
||||
private boolean mWaitingForConfirmation = false;
|
||||
private int mEncryptionRequestQuality;
|
||||
private boolean mEncryptionRequestDisabled;
|
||||
private boolean mForChangeCredRequiredForBoot = false;
|
||||
private String mUserPassword;
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private FingerprintManager mFingerprintManager;
|
||||
private int mUserId;
|
||||
private boolean mHideDrawer = false;
|
||||
private ManagedLockPasswordProvider mManagedPasswordProvider;
|
||||
private boolean mIsSetNewPassword = false;
|
||||
private UserManager mUserManager;
|
||||
|
||||
protected boolean mForFingerprint = false;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.CHOOSE_LOCK_GENERIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String chooseLockAction = getActivity().getIntent().getAction();
|
||||
mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
|
||||
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
mKeyStore = KeyStore.getInstance();
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
|
||||
mLockPatternUtils = new LockPatternUtils(getActivity());
|
||||
mIsSetNewPassword = ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
|
||||
|| ACTION_SET_NEW_PASSWORD.equals(chooseLockAction);
|
||||
|
||||
// Defaults to needing to confirm credentials
|
||||
final boolean confirmCredentials = getActivity().getIntent()
|
||||
.getBooleanExtra(CONFIRM_CREDENTIALS, true);
|
||||
if (getActivity() instanceof ChooseLockGeneric.InternalActivity) {
|
||||
mPasswordConfirmed = !confirmCredentials;
|
||||
}
|
||||
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
|
||||
|
||||
mHasChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mChallenge = getActivity().getIntent().getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
mForFingerprint = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
|
||||
mUserManager = UserManager.get(getActivity());
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
|
||||
mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
|
||||
mEncryptionRequestQuality = savedInstanceState.getInt(ENCRYPT_REQUESTED_QUALITY);
|
||||
mEncryptionRequestDisabled = savedInstanceState.getBoolean(
|
||||
ENCRYPT_REQUESTED_DISABLED);
|
||||
}
|
||||
|
||||
// a) If this is started from other user, use that user id.
|
||||
// b) If this is started from the same user, read the extra if this is launched
|
||||
// from Settings app itself.
|
||||
// c) Otherwise, use UserHandle.myUserId().
|
||||
mUserId = Utils.getSecureTargetUser(
|
||||
getActivity().getActivityToken(),
|
||||
UserManager.get(getActivity()),
|
||||
getArguments(),
|
||||
getActivity().getIntent().getExtras()).getIdentifier();
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
|
||||
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
|
||||
getActivity().setTitle(R.string.lock_settings_picker_title_profile);
|
||||
}
|
||||
|
||||
mManagedPasswordProvider = ManagedLockPasswordProvider.get(getActivity(), mUserId);
|
||||
|
||||
if (mPasswordConfirmed) {
|
||||
updatePreferencesOrFinish();
|
||||
if (mForChangeCredRequiredForBoot) {
|
||||
maybeEnableEncryption(mLockPatternUtils.getKeyguardStoredPasswordQuality(
|
||||
mUserId), false);
|
||||
}
|
||||
} else if (!mWaitingForConfirmation) {
|
||||
ChooseLockSettingsHelper helper =
|
||||
new ChooseLockSettingsHelper(this.getActivity(), this);
|
||||
boolean managedProfileWithUnifiedLock =
|
||||
UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
&& !mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId);
|
||||
if (managedProfileWithUnifiedLock
|
||||
|| !helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
|
||||
getString(R.string.unlock_set_unlock_launch_picker_title), true, mUserId)) {
|
||||
mPasswordConfirmed = true; // no password set, so no need to confirm
|
||||
updatePreferencesOrFinish();
|
||||
} else {
|
||||
mWaitingForConfirmation = true;
|
||||
}
|
||||
}
|
||||
addHeaderView();
|
||||
}
|
||||
|
||||
protected void addHeaderView() {
|
||||
if (mForFingerprint) {
|
||||
setHeaderView(R.layout.choose_lock_generic_fingerprint_header);
|
||||
if (mIsSetNewPassword) {
|
||||
((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description))
|
||||
.setText(R.string.fingerprint_unlock_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
|
||||
if (!isUnlockMethodSecure(key) && mLockPatternUtils.isSecure(mUserId)) {
|
||||
// Show the disabling FRP warning only when the user is switching from a secure
|
||||
// unlock method to an insecure one
|
||||
showFactoryResetProtectionWarningDialog(key);
|
||||
return true;
|
||||
} else if (KEY_SKIP_FINGERPRINT.equals(key)) {
|
||||
Intent chooseLockGenericIntent = new Intent(getActivity(),
|
||||
ChooseLockGeneric.InternalActivity.class);
|
||||
chooseLockGenericIntent.setAction(getIntent().getAction());
|
||||
// Forward the target user id to ChooseLockGeneric.
|
||||
chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
|
||||
startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST);
|
||||
return true;
|
||||
} else {
|
||||
return setUnlockMethod(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the device has encryption already enabled, then ask the user if they
|
||||
* also want to encrypt the phone with this password.
|
||||
*
|
||||
* @param quality
|
||||
* @param disabled
|
||||
*/
|
||||
// TODO: why does this take disabled, its always called with a quality higher than
|
||||
// what makes sense with disabled == true
|
||||
private void maybeEnableEncryption(int quality, boolean disabled) {
|
||||
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
|
||||
if (UserManager.get(getActivity()).isAdminUser()
|
||||
&& mUserId == UserHandle.myUserId()
|
||||
&& LockPatternUtils.isDeviceEncryptionEnabled()
|
||||
&& !LockPatternUtils.isFileEncryptionEnabled()
|
||||
&& !dpm.getDoNotAskCredentialsOnBoot()) {
|
||||
mEncryptionRequestQuality = quality;
|
||||
mEncryptionRequestDisabled = disabled;
|
||||
// Get the intent that the encryption interstitial should start for creating
|
||||
// the new unlock method.
|
||||
Intent unlockMethodIntent = getIntentForUnlockMethod(quality);
|
||||
unlockMethodIntent.putExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT,
|
||||
mForChangeCredRequiredForBoot);
|
||||
final Context context = getActivity();
|
||||
// If accessibility is enabled and the user hasn't seen this dialog before, set the
|
||||
// default state to agree with that which is compatible with accessibility
|
||||
// (password not required).
|
||||
final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
|
||||
final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
|
||||
Intent intent = getEncryptionInterstitialIntent(context, quality, required,
|
||||
unlockMethodIntent);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
|
||||
mForFingerprint);
|
||||
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
|
||||
startActivityForResult(
|
||||
intent,
|
||||
mIsSetNewPassword && mHasChallenge
|
||||
? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
|
||||
: ENABLE_ENCRYPTION_REQUEST);
|
||||
} else {
|
||||
if (mForChangeCredRequiredForBoot) {
|
||||
// Welp, couldn't change it. Oh well.
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
updateUnlockMethodAndFinish(quality, disabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
mWaitingForConfirmation = false;
|
||||
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
|
||||
mPasswordConfirmed = true;
|
||||
mUserPassword = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
updatePreferencesOrFinish();
|
||||
if (mForChangeCredRequiredForBoot) {
|
||||
if (!TextUtils.isEmpty(mUserPassword)) {
|
||||
maybeEnableEncryption(
|
||||
mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId), false);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
} else if (requestCode == CHOOSE_LOCK_REQUEST
|
||||
|| requestCode == ENABLE_ENCRYPTION_REQUEST) {
|
||||
if (resultCode != RESULT_CANCELED || mForChangeCredRequiredForBoot) {
|
||||
getActivity().setResult(resultCode, data);
|
||||
finish();
|
||||
}
|
||||
} else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
|
||||
&& resultCode == FingerprintEnrollBase.RESULT_FINISHED) {
|
||||
Intent intent = getFindSensorIntent(getActivity());
|
||||
if (data != null) {
|
||||
intent.putExtras(data.getExtras());
|
||||
}
|
||||
// Forward the target user id to fingerprint setup page.
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} else if (requestCode == SKIP_FINGERPRINT_REQUEST) {
|
||||
if (resultCode != RESULT_CANCELED) {
|
||||
getActivity().setResult(
|
||||
resultCode == RESULT_FINISHED ? RESULT_OK : resultCode, data);
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
if (requestCode == Activity.RESULT_CANCELED && mForChangeCredRequiredForBoot) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected Intent getFindSensorIntent(Context context) {
|
||||
return new Intent(context, FingerprintEnrollFindSensor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// Saved so we don't force user to re-enter their password if configuration changes
|
||||
outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
|
||||
outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
|
||||
outState.putInt(ENCRYPT_REQUESTED_QUALITY, mEncryptionRequestQuality);
|
||||
outState.putBoolean(ENCRYPT_REQUESTED_DISABLED, mEncryptionRequestDisabled);
|
||||
}
|
||||
|
||||
private void updatePreferencesOrFinish() {
|
||||
Intent intent = getActivity().getIntent();
|
||||
int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
|
||||
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);
|
||||
final boolean hideDisabledPrefs = intent.getBooleanExtra(
|
||||
HIDE_DISABLED_PREFS, false);
|
||||
final PreferenceScreen prefScreen = getPreferenceScreen();
|
||||
if (prefScreen != null) {
|
||||
prefScreen.removeAll();
|
||||
}
|
||||
addPreferences();
|
||||
disableUnusablePreferences(quality, hideDisabledPrefs);
|
||||
updatePreferenceText();
|
||||
updateCurrentPreference();
|
||||
updatePreferenceSummaryIfNeeded();
|
||||
} else {
|
||||
updateUnlockMethodAndFinish(quality, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addPreferences() {
|
||||
addPreferencesFromResource(R.xml.security_settings_picker);
|
||||
|
||||
// Used for testing purposes
|
||||
findPreference(KEY_UNLOCK_SET_NONE).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);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
|
||||
Preference managed = findPreference(KEY_UNLOCK_SET_MANAGED);
|
||||
managed.setTitle(mManagedPasswordProvider.getPickerOptionTitle(mForFingerprint));
|
||||
} else {
|
||||
removePreference(KEY_UNLOCK_SET_MANAGED);
|
||||
}
|
||||
|
||||
if (!(mForFingerprint && mIsSetNewPassword)) {
|
||||
removePreference(KEY_SKIP_FINGERPRINT);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCurrentPreference() {
|
||||
String currentKey = getKeyForCurrent();
|
||||
Preference preference = findPreference(currentKey);
|
||||
if (preference != null) {
|
||||
preference.setSummary(R.string.current_screen_lock);
|
||||
}
|
||||
}
|
||||
|
||||
private String getKeyForCurrent() {
|
||||
final int credentialOwner = UserManager.get(getContext())
|
||||
.getCredentialOwnerProfile(mUserId);
|
||||
if (mLockPatternUtils.isLockScreenDisabled(credentialOwner)) {
|
||||
return KEY_UNLOCK_SET_OFF;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/***
|
||||
* Disables preferences that are less secure than required quality. The actual
|
||||
* implementation is in disableUnusablePreferenceImpl.
|
||||
*
|
||||
* @param quality the requested quality.
|
||||
* @param hideDisabledPrefs if false preferences show why they were disabled; otherwise
|
||||
* they're not shown at all.
|
||||
*/
|
||||
protected void disableUnusablePreferences(final int quality, boolean hideDisabledPrefs) {
|
||||
disableUnusablePreferencesImpl(quality, hideDisabledPrefs);
|
||||
}
|
||||
|
||||
/***
|
||||
* Disables preferences that are less secure than required quality.
|
||||
*
|
||||
* @param quality the requested quality.
|
||||
* @param hideDisabled whether to hide disable screen lock options.
|
||||
*/
|
||||
protected void disableUnusablePreferencesImpl(final int quality,
|
||||
boolean hideDisabled) {
|
||||
final PreferenceScreen entries = getPreferenceScreen();
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
if (hideDisabled) {
|
||||
visible = enabled;
|
||||
}
|
||||
if (!visible) {
|
||||
entries.removePreference(pref);
|
||||
} else if (disabledByAdmin && enforcedAdmin != null) {
|
||||
((RestrictedPreference) pref).setDisabledByAdmin(enforcedAdmin);
|
||||
} else if (!enabled) {
|
||||
// we need to setDisabledByAdmin to null first to disable the padlock
|
||||
// in case it was set earlier.
|
||||
((RestrictedPreference) pref).setDisabledByAdmin(null);
|
||||
pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
|
||||
pref.setEnabled(false);
|
||||
} else {
|
||||
((RestrictedPreference) pref).setDisabledByAdmin(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreferenceSummaryIfNeeded() {
|
||||
// On a default block encrypted device with accessibility, add a warning
|
||||
// that your data is not credential encrypted
|
||||
if (!StorageManager.isBlockEncrypted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (StorageManager.isNonDefaultBlockEncrypted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AccessibilityManager.getInstance(getActivity()).getEnabledAccessibilityServiceList(
|
||||
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Intent getLockManagedPasswordIntent(String password) {
|
||||
return mManagedPasswordProvider.createIntent(false, password);
|
||||
}
|
||||
|
||||
protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) {
|
||||
ChooseLockPassword.IntentBuilder builder =
|
||||
new ChooseLockPassword.IntentBuilder(getContext())
|
||||
.setPasswordQuality(quality)
|
||||
.setPasswordLengthRange(minLength, maxLength)
|
||||
.setUserId(mUserId);
|
||||
if (mHasChallenge) {
|
||||
builder.setChallenge(mChallenge);
|
||||
} else {
|
||||
builder.setPassword(mUserPassword);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected Intent getLockPatternIntent() {
|
||||
ChooseLockPattern.IntentBuilder builder =
|
||||
new ChooseLockPattern.IntentBuilder(getContext())
|
||||
.setUserId(mUserId);
|
||||
if (mHasChallenge) {
|
||||
builder.setChallenge(mChallenge);
|
||||
} else {
|
||||
builder.setPattern(mUserPassword);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected Intent getEncryptionInterstitialIntent(Context context, int quality,
|
||||
boolean required, Intent unlockMethodIntent) {
|
||||
return EncryptionInterstitial.createStartIntent(context, quality, required,
|
||||
unlockMethodIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes an activity to change the user's pattern, password or PIN based on given quality
|
||||
* and minimum quality specified by DevicePolicyManager. If quality is
|
||||
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, password is cleared.
|
||||
*
|
||||
* @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
|
||||
* {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}
|
||||
*/
|
||||
void updateUnlockMethodAndFinish(int quality, boolean disabled) {
|
||||
// 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);
|
||||
Intent intent = getIntentForUnlockMethod(quality);
|
||||
if (intent != null) {
|
||||
startActivityForResult(intent,
|
||||
mIsSetNewPassword && mHasChallenge
|
||||
? CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
|
||||
: CHOOSE_LOCK_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
|
||||
mLockPatternUtils.setSeparateProfileChallengeEnabled(mUserId, true, mUserPassword);
|
||||
mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId);
|
||||
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
|
||||
getActivity().setResult(Activity.RESULT_OK);
|
||||
removeAllFingerprintForUserAndFinish(mUserId);
|
||||
} else {
|
||||
removeAllFingerprintForUserAndFinish(mUserId);
|
||||
}
|
||||
}
|
||||
|
||||
private Intent getIntentForUnlockMethod(int quality) {
|
||||
Intent intent = null;
|
||||
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
|
||||
intent = getLockManagedPasswordIntent(mUserPassword);
|
||||
} else if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
|
||||
int minLength = mDPM.getPasswordMinimumLength(null, mUserId);
|
||||
if (minLength < MIN_PASSWORD_LENGTH) {
|
||||
minLength = MIN_PASSWORD_LENGTH;
|
||||
}
|
||||
final int maxLength = mDPM.getPasswordMaximumLength(quality);
|
||||
intent = getLockPasswordIntent(quality, minLength, maxLength);
|
||||
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
|
||||
intent = getLockPatternIntent();
|
||||
}
|
||||
if (intent != null) {
|
||||
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
private void removeAllFingerprintForUserAndFinish(final int userId) {
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
|
||||
mFingerprintManager.setActiveUser(userId);
|
||||
// For the purposes of M and N, groupId is the same as userId.
|
||||
final int groupId = userId;
|
||||
Fingerprint finger = new Fingerprint(null, groupId, 0, 0);
|
||||
mFingerprintManager.remove(finger, userId,
|
||||
new RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Fingerprint fp, int errMsgId,
|
||||
CharSequence errString) {
|
||||
Log.v(TAG, "Fingerprint removed: " + fp.getFingerId());
|
||||
if (fp.getFingerId() == 0) {
|
||||
removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(Fingerprint fingerprint) {
|
||||
if (fingerprint.getFingerId() == 0) {
|
||||
removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No fingerprints in this user, we may also want to delete managed profile
|
||||
// fingerprints
|
||||
removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
|
||||
}
|
||||
} else {
|
||||
// The removal callback will call finish, once all fingerprints are removed.
|
||||
// We need to wait for that to occur, otherwise, the UI will still show that
|
||||
// fingerprints exist even though they are (about to) be removed depending on
|
||||
// the race condition.
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId) {
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
mFingerprintManager.setActiveUser(UserHandle.myUserId());
|
||||
}
|
||||
boolean hasChildProfile = false;
|
||||
if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
|
||||
// Current user is primary profile, remove work profile fingerprints if necessary
|
||||
final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
|
||||
final int profilesSize = profiles.size();
|
||||
for (int i = 0; i < profilesSize; i++) {
|
||||
final UserInfo userInfo = profiles.get(i);
|
||||
if (userInfo.isManagedProfile() && !mLockPatternUtils
|
||||
.isSeparateProfileChallengeEnabled(userInfo.id)) {
|
||||
removeAllFingerprintForUserAndFinish(userInfo.id);
|
||||
hasChildProfile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasChildProfile) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHelpResource() {
|
||||
return R.string.help_url_choose_lockscreen;
|
||||
}
|
||||
|
||||
private int getResIdForFactoryResetProtectionWarningTitle() {
|
||||
boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId);
|
||||
return isProfile ? R.string.unlock_disable_frp_warning_title_profile
|
||||
: R.string.unlock_disable_frp_warning_title;
|
||||
}
|
||||
|
||||
private int getResIdForFactoryResetProtectionWarningMessage() {
|
||||
final boolean hasFingerprints;
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
hasFingerprints = mFingerprintManager.hasEnrolledFingerprints(mUserId);
|
||||
} else {
|
||||
hasFingerprints = false;
|
||||
}
|
||||
boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mUserId);
|
||||
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)) {
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||
if (hasFingerprints && isProfile) {
|
||||
return R.string
|
||||
.unlock_disable_frp_warning_content_pattern_fingerprint_profile;
|
||||
} else if (hasFingerprints && !isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_pattern_fingerprint;
|
||||
} else if (isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_pattern_profile;
|
||||
} else {
|
||||
return R.string.unlock_disable_frp_warning_content_pattern;
|
||||
}
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||
if (hasFingerprints && isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_pin_fingerprint_profile;
|
||||
} else if (hasFingerprints && !isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_pin_fingerprint;
|
||||
} else if (isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_pin_profile;
|
||||
} else {
|
||||
return R.string.unlock_disable_frp_warning_content_pin;
|
||||
}
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||
if (hasFingerprints && isProfile) {
|
||||
return R.string
|
||||
.unlock_disable_frp_warning_content_password_fingerprint_profile;
|
||||
} else if (hasFingerprints && !isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_password_fingerprint;
|
||||
} else if (isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_password_profile;
|
||||
} else {
|
||||
return R.string.unlock_disable_frp_warning_content_password;
|
||||
}
|
||||
default:
|
||||
if (hasFingerprints && isProfile) {
|
||||
return R.string
|
||||
.unlock_disable_frp_warning_content_unknown_fingerprint_profile;
|
||||
} else if (hasFingerprints && !isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_unknown_fingerprint;
|
||||
} else if (isProfile) {
|
||||
return R.string.unlock_disable_frp_warning_content_unknown_profile;
|
||||
} else {
|
||||
return R.string.unlock_disable_frp_warning_content_unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnlockMethodSecure(String unlockMethod) {
|
||||
return !(KEY_UNLOCK_SET_OFF.equals(unlockMethod) ||
|
||||
KEY_UNLOCK_SET_NONE.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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
|
||||
int title = getResIdForFactoryResetProtectionWarningTitle();
|
||||
int message = getResIdForFactoryResetProtectionWarningMessage();
|
||||
FactoryResetProtectionWarningDialog dialog =
|
||||
FactoryResetProtectionWarningDialog.newInstance(
|
||||
title, message, unlockMethodToSet);
|
||||
dialog.show(getChildFragmentManager(), TAG_FRP_WARNING_DIALOG);
|
||||
}
|
||||
|
||||
public static class FactoryResetProtectionWarningDialog extends InstrumentedDialogFragment {
|
||||
|
||||
private static final String ARG_TITLE_RES = "titleRes";
|
||||
private static final String ARG_MESSAGE_RES = "messageRes";
|
||||
private static final String ARG_UNLOCK_METHOD_TO_SET = "unlockMethodToSet";
|
||||
|
||||
public static FactoryResetProtectionWarningDialog newInstance(
|
||||
int titleRes, int messageRes, String unlockMethodToSet) {
|
||||
FactoryResetProtectionWarningDialog frag =
|
||||
new FactoryResetProtectionWarningDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_TITLE_RES, titleRes);
|
||||
args.putInt(ARG_MESSAGE_RES, messageRes);
|
||||
args.putString(ARG_UNLOCK_METHOD_TO_SET, unlockMethodToSet);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(FragmentManager manager, String tag) {
|
||||
if (manager.findFragmentByTag(tag) == null) {
|
||||
// Prevent opening multiple dialogs if tapped on button quickly
|
||||
super.show(manager, tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.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();
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.DIALOG_FRP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
914
src/com/android/settings/password/ChooseLockPassword.java
Normal file
914
src/com/android/settings/password/ChooseLockPassword.java
Normal file
@@ -0,0 +1,914 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
import com.android.setupwizardlib.GlifLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChooseLockPassword extends SettingsActivity {
|
||||
public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
|
||||
public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
|
||||
public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
|
||||
public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase";
|
||||
public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase";
|
||||
public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
|
||||
public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
|
||||
public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
|
||||
|
||||
private static final String TAG = "ChooseLockPassword";
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
public static class IntentBuilder {
|
||||
|
||||
private final Intent mIntent;
|
||||
|
||||
public IntentBuilder(Context context) {
|
||||
mIntent = new Intent(context, ChooseLockPassword.class);
|
||||
mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
|
||||
mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
|
||||
}
|
||||
|
||||
public IntentBuilder setPasswordQuality(int quality) {
|
||||
mIntent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setPasswordLengthRange(int min, int max) {
|
||||
mIntent.putExtra(PASSWORD_MIN_KEY, min);
|
||||
mIntent.putExtra(PASSWORD_MAX_KEY, max);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setUserId(int userId) {
|
||||
mIntent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setChallenge(long challenge) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setPassword(String password) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* package */ Class<? extends Fragment> getFragmentClass() {
|
||||
return ChooseLockPasswordFragment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
CharSequence msg = getText(R.string.lockpassword_choose_your_password_header);
|
||||
setTitle(msg);
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
public static class ChooseLockPasswordFragment extends InstrumentedPreferenceFragment
|
||||
implements OnClickListener, OnEditorActionListener, TextWatcher,
|
||||
SaveAndFinishWorker.Listener {
|
||||
private static final String KEY_FIRST_PIN = "first_pin";
|
||||
private static final String KEY_UI_STAGE = "ui_stage";
|
||||
private static final String KEY_CURRENT_PASSWORD = "current_password";
|
||||
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
|
||||
|
||||
private String mCurrentPassword;
|
||||
private String mChosenPassword;
|
||||
private boolean mHasChallenge;
|
||||
private long mChallenge;
|
||||
private EditText mPasswordEntry;
|
||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||
private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
|
||||
private int mPasswordMaxLength = 16;
|
||||
private int mPasswordMinLetters = 0;
|
||||
private int mPasswordMinUpperCase = 0;
|
||||
private int mPasswordMinLowerCase = 0;
|
||||
private int mPasswordMinSymbols = 0;
|
||||
private int mPasswordMinNumeric = 0;
|
||||
private int mPasswordMinNonLetter = 0;
|
||||
private int mPasswordMinLengthToFulfillAllPolicies = 0;
|
||||
private int mUserId;
|
||||
private boolean mHideDrawer = false;
|
||||
/**
|
||||
* Password requirements that we need to verify.
|
||||
*/
|
||||
private int[] mPasswordRequirements;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private SaveAndFinishWorker mSaveAndFinishWorker;
|
||||
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private Stage mUiStage = Stage.Introduction;
|
||||
private PasswordRequirementAdapter mPasswordRequirementAdapter;
|
||||
|
||||
private TextView mHeaderText;
|
||||
private String mFirstPin;
|
||||
private RecyclerView mPasswordRestrictionView;
|
||||
private boolean mIsAlphaMode;
|
||||
private Button mCancelButton;
|
||||
private Button mNextButton;
|
||||
|
||||
private TextChangedHandler mTextChangedHandler;
|
||||
|
||||
private static final int CONFIRM_EXISTING_REQUEST = 58;
|
||||
static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
|
||||
private static final int MIN_LETTER_IN_PASSWORD = 0;
|
||||
private static final int MIN_UPPER_LETTERS_IN_PASSWORD = 1;
|
||||
private static final int MIN_LOWER_LETTERS_IN_PASSWORD = 2;
|
||||
private static final int MIN_SYMBOLS_IN_PASSWORD = 3;
|
||||
private static final int MIN_NUMBER_IN_PASSWORD = 4;
|
||||
private static final int MIN_NON_LETTER_IN_PASSWORD = 5;
|
||||
|
||||
// Error code returned from {@link #validatePassword(String)}.
|
||||
private static final int NO_ERROR = 0;
|
||||
private static final int CONTAIN_INVALID_CHARACTERS = 1 << 0;
|
||||
private static final int TOO_SHORT = 1 << 1;
|
||||
private static final int TOO_LONG = 1 << 2;
|
||||
private static final int CONTAIN_NON_DIGITS = 1 << 3;
|
||||
private static final int CONTAIN_SEQUENTIAL_DIGITS = 1 << 4;
|
||||
private static final int RECENTLY_USED = 1 << 5;
|
||||
private static final int NOT_ENOUGH_LETTER = 1 << 6;
|
||||
private static final int NOT_ENOUGH_UPPER_CASE = 1 << 7;
|
||||
private static final int NOT_ENOUGH_LOWER_CASE = 1 << 8;
|
||||
private static final int NOT_ENOUGH_DIGITS = 1 << 9;
|
||||
private static final int NOT_ENOUGH_SYMBOLS = 1 << 10;
|
||||
private static final int NOT_ENOUGH_NON_LETTER = 1 << 11;
|
||||
|
||||
/**
|
||||
* Keep track internally of where the user is in choosing a pattern.
|
||||
*/
|
||||
protected enum Stage {
|
||||
|
||||
Introduction(R.string.lockpassword_choose_your_password_header,
|
||||
R.string.lockpassword_choose_your_pin_header,
|
||||
R.string.lockpassword_continue_label),
|
||||
|
||||
NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
|
||||
R.string.lockpassword_confirm_your_pin_header,
|
||||
R.string.lockpassword_ok_label),
|
||||
|
||||
ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
|
||||
R.string.lockpassword_confirm_pins_dont_match,
|
||||
R.string.lockpassword_continue_label);
|
||||
|
||||
Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
|
||||
this.alphaHint = hintInAlpha;
|
||||
this.numericHint = hintInNumeric;
|
||||
this.buttonText = nextButtonText;
|
||||
}
|
||||
|
||||
public final int alphaHint;
|
||||
public final int numericHint;
|
||||
public final int buttonText;
|
||||
}
|
||||
|
||||
// required constructor for fragments
|
||||
public ChooseLockPasswordFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mLockPatternUtils = new LockPatternUtils(getActivity());
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (!(getActivity() instanceof ChooseLockPassword)) {
|
||||
throw new SecurityException("Fragment contained in wrong activity");
|
||||
}
|
||||
// Only take this argument into account if it belongs to the current profile.
|
||||
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
|
||||
processPasswordRequirements(intent);
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
|
||||
SaveAndFinishWorker w = new SaveAndFinishWorker();
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
String current = intent.getStringExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
w.setBlocking(true);
|
||||
w.setListener(this);
|
||||
w.start(mChooseLockSettingsHelper.utils(), required,
|
||||
false, 0, current, current, mRequestedQuality, mUserId);
|
||||
}
|
||||
mTextChangedHandler = new TextChangedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.choose_lock_password, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mCancelButton = (Button) view.findViewById(R.id.cancel_button);
|
||||
mCancelButton.setOnClickListener(this);
|
||||
mNextButton = (Button) view.findViewById(R.id.next_button);
|
||||
mNextButton.setOnClickListener(this);
|
||||
|
||||
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
|
||||
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
|
||||
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
|
||||
|
||||
setupPasswordRequirementsView(view);
|
||||
|
||||
mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mPasswordEntry = (EditText) view.findViewById(R.id.password_entry);
|
||||
mPasswordEntry.setOnEditorActionListener(this);
|
||||
mPasswordEntry.addTextChangedListener(this);
|
||||
mPasswordEntry.requestFocus();
|
||||
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
mHeaderText = (TextView) view.findViewById(R.id.headerText);
|
||||
|
||||
int currentType = mPasswordEntry.getInputType();
|
||||
mPasswordEntry.setInputType(mIsAlphaMode ? currentType
|
||||
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
|
||||
|
||||
Intent intent = getActivity().getIntent();
|
||||
final boolean confirmCredentials = intent.getBooleanExtra(
|
||||
ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
|
||||
mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
mHasChallenge = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
if (savedInstanceState == null) {
|
||||
updateStage(Stage.Introduction);
|
||||
if (confirmCredentials) {
|
||||
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
|
||||
getString(R.string.unlock_set_unlock_launch_picker_title), true,
|
||||
mUserId);
|
||||
}
|
||||
} else {
|
||||
// restore from previous state
|
||||
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
|
||||
final String state = savedInstanceState.getString(KEY_UI_STAGE);
|
||||
if (state != null) {
|
||||
mUiStage = Stage.valueOf(state);
|
||||
updateStage(mUiStage);
|
||||
}
|
||||
|
||||
if (mCurrentPassword == null) {
|
||||
mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD);
|
||||
}
|
||||
|
||||
// Re-attach to the exiting worker if there is one.
|
||||
mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH);
|
||||
}
|
||||
|
||||
if (activity instanceof SettingsActivity) {
|
||||
final SettingsActivity sa = (SettingsActivity) activity;
|
||||
int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
|
||||
: R.string.lockpassword_choose_your_pin_header;
|
||||
CharSequence title = getText(id);
|
||||
sa.setTitle(title);
|
||||
((GlifLayout) view).setHeaderText(title);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupPasswordRequirementsView(View view) {
|
||||
// Construct passwordRequirements and requirementDescriptions.
|
||||
List<Integer> passwordRequirements = new ArrayList<>();
|
||||
List<String> requirementDescriptions = new ArrayList<>();
|
||||
if (mPasswordMinUpperCase > 0) {
|
||||
passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
|
||||
mPasswordMinUpperCase));
|
||||
}
|
||||
if (mPasswordMinLowerCase > 0) {
|
||||
passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
|
||||
mPasswordMinLowerCase));
|
||||
}
|
||||
if (mPasswordMinLetters > 0) {
|
||||
if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) {
|
||||
passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
|
||||
mPasswordMinLetters));
|
||||
}
|
||||
}
|
||||
if (mPasswordMinNumeric > 0) {
|
||||
passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
|
||||
mPasswordMinNumeric));
|
||||
}
|
||||
if (mPasswordMinSymbols > 0) {
|
||||
passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
|
||||
mPasswordMinSymbols));
|
||||
}
|
||||
if (mPasswordMinNonLetter > 0) {
|
||||
if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) {
|
||||
passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
|
||||
requirementDescriptions.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
|
||||
|
||||
mPasswordMinNonLetter));
|
||||
}
|
||||
}
|
||||
// Convert list to array.
|
||||
mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
|
||||
mPasswordRestrictionView =
|
||||
(RecyclerView) view.findViewById(R.id.password_requirements_view);
|
||||
mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mPasswordRequirementAdapter = new PasswordRequirementAdapter();
|
||||
mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.CHOOSE_LOCK_PASSWORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateStage(mUiStage);
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
mSaveAndFinishWorker.setListener(this);
|
||||
} else {
|
||||
mPasswordEntry.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
mSaveAndFinishWorker.setListener(null);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(KEY_UI_STAGE, mUiStage.name());
|
||||
outState.putString(KEY_FIRST_PIN, mFirstPin);
|
||||
outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case CONFIRM_EXISTING_REQUEST:
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
getActivity().setResult(RESULT_FINISHED);
|
||||
getActivity().finish();
|
||||
} else {
|
||||
mCurrentPassword = data.getStringExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected Intent getRedactionInterstitialIntent(Context context) {
|
||||
return RedactionInterstitial.createStartIntent(context, mUserId);
|
||||
}
|
||||
|
||||
protected void updateStage(Stage stage) {
|
||||
final Stage previousStage = mUiStage;
|
||||
mUiStage = stage;
|
||||
updateUi();
|
||||
|
||||
// If the stage changed, announce the header for accessibility. This
|
||||
// is a no-op when accessibility is disabled.
|
||||
if (previousStage != stage) {
|
||||
mHeaderText.announceForAccessibility(mHeaderText.getText());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
|
||||
*
|
||||
* @param intent the incoming intent
|
||||
*/
|
||||
private void processPasswordRequirements(Intent intent) {
|
||||
final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
mRequestedQuality), dpmPasswordQuality);
|
||||
mPasswordMinLength = Math.max(Math.max(
|
||||
LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
|
||||
intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)),
|
||||
mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
|
||||
mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
|
||||
mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
|
||||
mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters(
|
||||
mUserId));
|
||||
mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
|
||||
mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase(
|
||||
mUserId));
|
||||
mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
|
||||
mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase(
|
||||
mUserId));
|
||||
mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
|
||||
mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric(
|
||||
mUserId));
|
||||
mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
|
||||
mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols(
|
||||
mUserId));
|
||||
mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
|
||||
mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter(
|
||||
mUserId));
|
||||
|
||||
// Modify the value based on dpm policy.
|
||||
switch (dpmPasswordQuality) {
|
||||
case PASSWORD_QUALITY_ALPHABETIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
break;
|
||||
case PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
if (mPasswordMinNumeric == 0) {
|
||||
mPasswordMinNumeric = 1;
|
||||
}
|
||||
break;
|
||||
case PASSWORD_QUALITY_COMPLEX:
|
||||
// Reserve all the requirements.
|
||||
break;
|
||||
default:
|
||||
mPasswordMinNumeric = 0;
|
||||
mPasswordMinLetters = 0;
|
||||
mPasswordMinUpperCase = 0;
|
||||
mPasswordMinLowerCase = 0;
|
||||
mPasswordMinSymbols = 0;
|
||||
mPasswordMinNonLetter = 0;
|
||||
}
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates PIN and returns the validation result.
|
||||
*
|
||||
* @param password the raw password the user typed in
|
||||
* @return the validation result.
|
||||
*/
|
||||
private int validatePassword(String password) {
|
||||
int errorCode = NO_ERROR;
|
||||
|
||||
if (password.length() < mPasswordMinLength) {
|
||||
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
|
||||
errorCode |= TOO_SHORT;
|
||||
}
|
||||
} else if (password.length() > mPasswordMaxLength) {
|
||||
errorCode |= TOO_LONG;
|
||||
} else {
|
||||
// The length requirements are fulfilled.
|
||||
if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
|
||||
final int sequence = PasswordMetrics.maxLengthSequence(password);
|
||||
if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
|
||||
errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
|
||||
}
|
||||
}
|
||||
// Is the password recently used?
|
||||
if (mLockPatternUtils.checkPasswordHistory(password, mUserId)) {
|
||||
errorCode |= RECENTLY_USED;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow non-control Latin-1 characters only.
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
char c = password.charAt(i);
|
||||
if (c < 32 || c > 127) {
|
||||
errorCode |= CONTAIN_INVALID_CHARACTERS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
|
||||
|
||||
// Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
|
||||
// user finds some way to bring up soft keyboard.
|
||||
if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
|
||||
|| mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
if (metrics.letters > 0 || metrics.symbols > 0) {
|
||||
errorCode |= CONTAIN_NON_DIGITS;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the requirements one by one.
|
||||
for (int i = 0; i < mPasswordRequirements.length; i++) {
|
||||
int passwordRestriction = mPasswordRequirements[i];
|
||||
switch (passwordRestriction) {
|
||||
case MIN_LETTER_IN_PASSWORD:
|
||||
if (metrics.letters < mPasswordMinLetters) {
|
||||
errorCode |= NOT_ENOUGH_LETTER;
|
||||
}
|
||||
break;
|
||||
case MIN_UPPER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.upperCase < mPasswordMinUpperCase) {
|
||||
errorCode |= NOT_ENOUGH_UPPER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_LOWER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.lowerCase < mPasswordMinLowerCase) {
|
||||
errorCode |= NOT_ENOUGH_LOWER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_SYMBOLS_IN_PASSWORD:
|
||||
if (metrics.symbols < mPasswordMinSymbols) {
|
||||
errorCode |= NOT_ENOUGH_SYMBOLS;
|
||||
}
|
||||
break;
|
||||
case MIN_NUMBER_IN_PASSWORD:
|
||||
if (metrics.numeric < mPasswordMinNumeric) {
|
||||
errorCode |= NOT_ENOUGH_DIGITS;
|
||||
}
|
||||
break;
|
||||
case MIN_NON_LETTER_IN_PASSWORD:
|
||||
if (metrics.nonLetter < mPasswordMinNonLetter) {
|
||||
errorCode |= NOT_ENOUGH_NON_LETTER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void handleNext() {
|
||||
if (mSaveAndFinishWorker != null) return;
|
||||
mChosenPassword = mPasswordEntry.getText().toString();
|
||||
if (TextUtils.isEmpty(mChosenPassword)) {
|
||||
return;
|
||||
}
|
||||
if (mUiStage == Stage.Introduction) {
|
||||
if (validatePassword(mChosenPassword) == NO_ERROR) {
|
||||
mFirstPin = mChosenPassword;
|
||||
mPasswordEntry.setText("");
|
||||
updateStage(Stage.NeedToConfirm);
|
||||
}
|
||||
} else if (mUiStage == Stage.NeedToConfirm) {
|
||||
if (mFirstPin.equals(mChosenPassword)) {
|
||||
startSaveAndFinish();
|
||||
} else {
|
||||
CharSequence tmp = mPasswordEntry.getText();
|
||||
if (tmp != null) {
|
||||
Selection.setSelection((Spannable) tmp, 0, tmp.length());
|
||||
}
|
||||
updateStage(Stage.ConfirmWrong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setNextEnabled(boolean enabled) {
|
||||
mNextButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
protected void setNextText(int text) {
|
||||
mNextButton.setText(text);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.next_button:
|
||||
handleNext();
|
||||
break;
|
||||
|
||||
case R.id.cancel_button:
|
||||
getActivity().finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// Check if this was the result of hitting the enter or "done" key
|
||||
if (actionId == EditorInfo.IME_NULL
|
||||
|| actionId == EditorInfo.IME_ACTION_DONE
|
||||
|| actionId == EditorInfo.IME_ACTION_NEXT) {
|
||||
handleNext();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param errorCode error code returned from {@link #validatePassword(String)}.
|
||||
* @return an array of messages describing the error, important messages come first.
|
||||
*/
|
||||
private String[] convertErrorCodeToMessages(int errorCode) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_illegal_character));
|
||||
}
|
||||
if ((errorCode & CONTAIN_NON_DIGITS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_pin_contains_non_digits));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_UPPER_CASE) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
|
||||
mPasswordMinUpperCase));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_LOWER_CASE) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
|
||||
mPasswordMinLowerCase));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_LETTER) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
|
||||
mPasswordMinLetters));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_DIGITS) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
|
||||
mPasswordMinNumeric));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_SYMBOLS) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
|
||||
mPasswordMinSymbols));
|
||||
}
|
||||
if ((errorCode & NOT_ENOUGH_NON_LETTER) > 0) {
|
||||
messages.add(getResources().getQuantityString(
|
||||
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
|
||||
mPasswordMinNonLetter));
|
||||
}
|
||||
if ((errorCode & TOO_SHORT) > 0) {
|
||||
messages.add(getString(mIsAlphaMode ?
|
||||
R.string.lockpassword_password_too_short
|
||||
: R.string.lockpassword_pin_too_short, mPasswordMinLength));
|
||||
}
|
||||
if ((errorCode & TOO_LONG) > 0) {
|
||||
messages.add(getString(mIsAlphaMode ?
|
||||
R.string.lockpassword_password_too_long
|
||||
: R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1));
|
||||
}
|
||||
if ((errorCode & CONTAIN_SEQUENTIAL_DIGITS) > 0) {
|
||||
messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
|
||||
}
|
||||
if ((errorCode & RECENTLY_USED) > 0) {
|
||||
messages.add(getString((mIsAlphaMode) ? R.string.lockpassword_password_recently_used
|
||||
: R.string.lockpassword_pin_recently_used));
|
||||
}
|
||||
return messages.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private int getMinLengthToFulfillAllPolicies() {
|
||||
final int minLengthForLetters = Math.max(mPasswordMinLetters,
|
||||
mPasswordMinUpperCase + mPasswordMinLowerCase);
|
||||
final int minLengthForNonLetters = Math.max(mPasswordMinNonLetter,
|
||||
mPasswordMinSymbols + mPasswordMinNumeric);
|
||||
return minLengthForLetters + minLengthForNonLetters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the hint based on current Stage and length of password entry
|
||||
*/
|
||||
private void updateUi() {
|
||||
final boolean canInput = mSaveAndFinishWorker == null;
|
||||
String password = mPasswordEntry.getText().toString();
|
||||
final int length = password.length();
|
||||
if (mUiStage == Stage.Introduction) {
|
||||
mPasswordRestrictionView.setVisibility(View.VISIBLE);
|
||||
final int errorCode = validatePassword(password);
|
||||
String[] messages = convertErrorCodeToMessages(errorCode);
|
||||
// Update the fulfillment of requirements.
|
||||
mPasswordRequirementAdapter.setRequirements(messages);
|
||||
// Enable/Disable the next button accordingly.
|
||||
setNextEnabled(errorCode == NO_ERROR);
|
||||
} else {
|
||||
// Hide password requirement view when we are just asking user to confirm the pw.
|
||||
mPasswordRestrictionView.setVisibility(View.GONE);
|
||||
setHeaderText(getString(
|
||||
mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint));
|
||||
setNextEnabled(canInput && length > 0);
|
||||
}
|
||||
setNextText(mUiStage.buttonText);
|
||||
mPasswordEntryInputDisabler.setInputEnabled(canInput);
|
||||
}
|
||||
|
||||
private void setHeaderText(String text) {
|
||||
// Only set the text if it is different than the existing one to avoid announcing again.
|
||||
if (!TextUtils.isEmpty(mHeaderText.getText())
|
||||
&& mHeaderText.getText().toString().equals(text)) {
|
||||
return;
|
||||
}
|
||||
mHeaderText.setText(text);
|
||||
}
|
||||
|
||||
public void afterTextChanged(Editable s) {
|
||||
// Changing the text while error displayed resets to NeedToConfirm state
|
||||
if (mUiStage == Stage.ConfirmWrong) {
|
||||
mUiStage = Stage.NeedToConfirm;
|
||||
}
|
||||
// Schedule the UI update.
|
||||
mTextChangedHandler.notifyAfterTextChanged();
|
||||
}
|
||||
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
private void startSaveAndFinish() {
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
|
||||
return;
|
||||
}
|
||||
|
||||
mPasswordEntryInputDisabler.setInputEnabled(false);
|
||||
setNextEnabled(false);
|
||||
|
||||
mSaveAndFinishWorker = new SaveAndFinishWorker();
|
||||
mSaveAndFinishWorker.setListener(this);
|
||||
|
||||
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
mSaveAndFinishWorker.start(mLockPatternUtils, required, mHasChallenge, mChallenge,
|
||||
mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
getActivity().setResult(RESULT_FINISHED, resultData);
|
||||
|
||||
if (!wasSecureBefore) {
|
||||
Intent intent = getRedactionInterstitialIntent(getActivity());
|
||||
if (intent != null) {
|
||||
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
class TextChangedHandler extends Handler {
|
||||
private static final int ON_TEXT_CHANGED = 1;
|
||||
private static final int DELAY_IN_MILLISECOND = 100;
|
||||
|
||||
/**
|
||||
* With the introduction of delay, we batch processing the text changed event to reduce
|
||||
* unnecessary UI updates.
|
||||
*/
|
||||
private void notifyAfterTextChanged() {
|
||||
removeMessages(ON_TEXT_CHANGED);
|
||||
sendEmptyMessageDelayed(ON_TEXT_CHANGED, DELAY_IN_MILLISECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
if (msg.what == ON_TEXT_CHANGED) {
|
||||
updateUi();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
|
||||
|
||||
private String mChosenPassword;
|
||||
private String mCurrentPassword;
|
||||
private int mRequestedQuality;
|
||||
|
||||
public void start(LockPatternUtils utils, boolean required,
|
||||
boolean hasChallenge, long challenge,
|
||||
String chosenPassword, String currentPassword, int requestedQuality, int userId) {
|
||||
prepare(utils, required, hasChallenge, challenge, userId);
|
||||
|
||||
mChosenPassword = chosenPassword;
|
||||
mCurrentPassword = currentPassword;
|
||||
mRequestedQuality = requestedQuality;
|
||||
mUserId = userId;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent saveAndVerifyInBackground() {
|
||||
Intent result = null;
|
||||
mUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality,
|
||||
mUserId);
|
||||
|
||||
if (mHasChallenge) {
|
||||
byte[] token;
|
||||
try {
|
||||
token = mUtils.verifyPassword(mChosenPassword, mChallenge, mUserId);
|
||||
} catch (RequestThrottledException e) {
|
||||
token = null;
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
Log.e(TAG, "critical: no token returned for known good password.");
|
||||
}
|
||||
|
||||
result = new Intent();
|
||||
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
775
src/com/android/settings/password/ChooseLockPattern.java
Normal file
775
src/com/android/settings/password/ChooseLockPattern.java
Normal file
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.internal.widget.LockPatternView.Cell;
|
||||
import com.android.internal.widget.LockPatternView.DisplayMode;
|
||||
import com.android.settings.EncryptionInterstitial;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
import com.android.setupwizardlib.GlifLayout;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* If the user has a lock pattern set already, makes them confirm the existing one.
|
||||
*
|
||||
* Then, prompts the user to choose a lock pattern:
|
||||
* - prompts for initial pattern
|
||||
* - asks for confirmation / restart
|
||||
* - saves chosen password when confirmed
|
||||
*/
|
||||
public class ChooseLockPattern extends SettingsActivity {
|
||||
/**
|
||||
* Used by the choose lock pattern wizard to indicate the wizard is
|
||||
* finished, and each activity in the wizard should finish.
|
||||
* <p>
|
||||
* Previously, each activity in the wizard would finish itself after
|
||||
* starting the next activity. However, this leads to broken 'Back'
|
||||
* behavior. So, now an activity does not finish itself until it gets this
|
||||
* result.
|
||||
*/
|
||||
static final int RESULT_FINISHED = RESULT_FIRST_USER;
|
||||
|
||||
private static final String TAG = "ChooseLockPattern";
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
public static class IntentBuilder {
|
||||
private final Intent mIntent;
|
||||
|
||||
public IntentBuilder(Context context) {
|
||||
mIntent = new Intent(context, ChooseLockPattern.class);
|
||||
mIntent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, false);
|
||||
mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
|
||||
}
|
||||
|
||||
public IntentBuilder setUserId(int userId) {
|
||||
mIntent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setChallenge(long challenge) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setPattern(String pattern) {
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
if (ChooseLockPatternFragment.class.getName().equals(fragmentName)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* package */ Class<? extends Fragment> getFragmentClass() {
|
||||
return ChooseLockPatternFragment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
super.onCreate(savedInstanceState);
|
||||
CharSequence msg = getText(R.string.lockpassword_choose_your_pattern_header);
|
||||
setTitle(msg);
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
// *** TODO ***
|
||||
// chooseLockPatternFragment.onKeyDown(keyCode, event);
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
public static class ChooseLockPatternFragment extends InstrumentedPreferenceFragment
|
||||
implements View.OnClickListener, SaveAndFinishWorker.Listener {
|
||||
|
||||
public static final int CONFIRM_EXISTING_REQUEST = 55;
|
||||
|
||||
// how long after a confirmation message is shown before moving on
|
||||
static final int INFORMATION_MSG_TIMEOUT_MS = 3000;
|
||||
|
||||
// how long we wait to clear a wrong pattern
|
||||
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
|
||||
|
||||
private static final int ID_EMPTY_MESSAGE = -1;
|
||||
|
||||
private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker";
|
||||
|
||||
private String mCurrentPattern;
|
||||
private boolean mHasChallenge;
|
||||
private long mChallenge;
|
||||
protected TextView mHeaderText;
|
||||
protected LockPatternView mLockPatternView;
|
||||
protected TextView mFooterText;
|
||||
private TextView mFooterLeftButton;
|
||||
private TextView mFooterRightButton;
|
||||
protected List<LockPatternView.Cell> mChosenPattern = null;
|
||||
private boolean mHideDrawer = false;
|
||||
|
||||
// ScrollView that contains title and header, only exist in land mode
|
||||
private ScrollView mTitleHeaderScrollView;
|
||||
|
||||
/**
|
||||
* The patten used during the help screen to show how to draw a pattern.
|
||||
*/
|
||||
private final List<LockPatternView.Cell> mAnimatePattern =
|
||||
Collections.unmodifiableList(Lists.newArrayList(
|
||||
LockPatternView.Cell.of(0, 0),
|
||||
LockPatternView.Cell.of(0, 1),
|
||||
LockPatternView.Cell.of(1, 1),
|
||||
LockPatternView.Cell.of(2, 1)
|
||||
));
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case CONFIRM_EXISTING_REQUEST:
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
getActivity().setResult(RESULT_FINISHED);
|
||||
getActivity().finish();
|
||||
} else {
|
||||
mCurrentPattern = data.getStringExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
}
|
||||
|
||||
updateStage(Stage.Introduction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setRightButtonEnabled(boolean enabled) {
|
||||
mFooterRightButton.setEnabled(enabled);
|
||||
}
|
||||
|
||||
protected void setRightButtonText(int text) {
|
||||
mFooterRightButton.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* The pattern listener that responds according to a user choosing a new
|
||||
* lock pattern.
|
||||
*/
|
||||
protected LockPatternView.OnPatternListener mChooseNewLockPatternListener =
|
||||
new LockPatternView.OnPatternListener() {
|
||||
|
||||
public void onPatternStart() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
patternInProgress();
|
||||
}
|
||||
|
||||
public void onPatternCleared() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
}
|
||||
|
||||
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
|
||||
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
|
||||
if (mChosenPattern == null) throw new IllegalStateException(
|
||||
"null chosen pattern in stage 'need to confirm");
|
||||
if (mChosenPattern.equals(pattern)) {
|
||||
updateStage(Stage.ChoiceConfirmed);
|
||||
} else {
|
||||
updateStage(Stage.ConfirmWrong);
|
||||
}
|
||||
} else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
|
||||
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
|
||||
updateStage(Stage.ChoiceTooShort);
|
||||
} else {
|
||||
mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
|
||||
updateStage(Stage.FirstChoiceValid);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
|
||||
+ "entering the pattern.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onPatternCellAdded(List<Cell> pattern) {
|
||||
|
||||
}
|
||||
|
||||
private void patternInProgress() {
|
||||
mHeaderText.setText(R.string.lockpattern_recording_inprogress);
|
||||
mFooterText.setText("");
|
||||
mFooterLeftButton.setEnabled(false);
|
||||
mFooterRightButton.setEnabled(false);
|
||||
|
||||
if (mTitleHeaderScrollView != null) {
|
||||
mTitleHeaderScrollView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mTitleHeaderScrollView.fullScroll(ScrollView.FOCUS_DOWN);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.CHOOSE_LOCK_PATTERN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The states of the left footer button.
|
||||
*/
|
||||
enum LeftButtonMode {
|
||||
Cancel(R.string.cancel, true),
|
||||
CancelDisabled(R.string.cancel, false),
|
||||
Retry(R.string.lockpattern_retry_button_text, true),
|
||||
RetryDisabled(R.string.lockpattern_retry_button_text, false),
|
||||
Gone(ID_EMPTY_MESSAGE, false);
|
||||
|
||||
|
||||
/**
|
||||
* @param text The displayed text for this mode.
|
||||
* @param enabled Whether the button should be enabled.
|
||||
*/
|
||||
LeftButtonMode(int text, boolean enabled) {
|
||||
this.text = text;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
final int text;
|
||||
final boolean enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The states of the right button.
|
||||
*/
|
||||
enum RightButtonMode {
|
||||
Continue(R.string.lockpattern_continue_button_text, true),
|
||||
ContinueDisabled(R.string.lockpattern_continue_button_text, false),
|
||||
Confirm(R.string.lockpattern_confirm_button_text, true),
|
||||
ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
|
||||
Ok(android.R.string.ok, true);
|
||||
|
||||
/**
|
||||
* @param text The displayed text for this mode.
|
||||
* @param enabled Whether the button should be enabled.
|
||||
*/
|
||||
RightButtonMode(int text, boolean enabled) {
|
||||
this.text = text;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
final int text;
|
||||
final boolean enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep track internally of where the user is in choosing a pattern.
|
||||
*/
|
||||
protected enum Stage {
|
||||
|
||||
Introduction(
|
||||
R.string.lockpattern_recording_intro_header,
|
||||
LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
|
||||
ID_EMPTY_MESSAGE, true),
|
||||
HelpScreen(
|
||||
R.string.lockpattern_settings_help_how_to_record,
|
||||
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
|
||||
ChoiceTooShort(
|
||||
R.string.lockpattern_recording_incorrect_too_short,
|
||||
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
|
||||
ID_EMPTY_MESSAGE, true),
|
||||
FirstChoiceValid(
|
||||
R.string.lockpattern_pattern_entered_header,
|
||||
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
|
||||
NeedToConfirm(
|
||||
R.string.lockpattern_need_to_confirm,
|
||||
LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
|
||||
ID_EMPTY_MESSAGE, true),
|
||||
ConfirmWrong(
|
||||
R.string.lockpattern_need_to_unlock_wrong,
|
||||
LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
|
||||
ID_EMPTY_MESSAGE, true),
|
||||
ChoiceConfirmed(
|
||||
R.string.lockpattern_pattern_confirmed_header,
|
||||
LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
|
||||
|
||||
|
||||
/**
|
||||
* @param headerMessage The message displayed at the top.
|
||||
* @param leftMode The mode of the left button.
|
||||
* @param rightMode The mode of the right button.
|
||||
* @param footerMessage The footer message.
|
||||
* @param patternEnabled Whether the pattern widget is enabled.
|
||||
*/
|
||||
Stage(int headerMessage,
|
||||
LeftButtonMode leftMode,
|
||||
RightButtonMode rightMode,
|
||||
int footerMessage, boolean patternEnabled) {
|
||||
this.headerMessage = headerMessage;
|
||||
this.leftMode = leftMode;
|
||||
this.rightMode = rightMode;
|
||||
this.footerMessage = footerMessage;
|
||||
this.patternEnabled = patternEnabled;
|
||||
}
|
||||
|
||||
final int headerMessage;
|
||||
final LeftButtonMode leftMode;
|
||||
final RightButtonMode rightMode;
|
||||
final int footerMessage;
|
||||
final boolean patternEnabled;
|
||||
}
|
||||
|
||||
private Stage mUiStage = Stage.Introduction;
|
||||
|
||||
private Runnable mClearPatternRunnable = new Runnable() {
|
||||
public void run() {
|
||||
mLockPatternView.clearPattern();
|
||||
}
|
||||
};
|
||||
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private SaveAndFinishWorker mSaveAndFinishWorker;
|
||||
private int mUserId;
|
||||
|
||||
private static final String KEY_UI_STAGE = "uiStage";
|
||||
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
|
||||
private static final String KEY_CURRENT_PATTERN = "currentPattern";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||
if (!(getActivity() instanceof ChooseLockPattern)) {
|
||||
throw new SecurityException("Fragment contained in wrong activity");
|
||||
}
|
||||
Intent intent = getActivity().getIntent();
|
||||
// Only take this argument into account if it belongs to the current profile.
|
||||
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) {
|
||||
SaveAndFinishWorker w = new SaveAndFinishWorker();
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
String current = intent.getStringExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
w.setBlocking(true);
|
||||
w.setListener(this);
|
||||
w.start(mChooseLockSettingsHelper.utils(), required,
|
||||
false, 0, LockPatternUtils.stringToPattern(current), current, mUserId);
|
||||
}
|
||||
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
final GlifLayout layout = (GlifLayout) inflater.inflate(
|
||||
R.layout.choose_lock_pattern, container, false);
|
||||
layout.setHeaderText(getActivity().getTitle());
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mHeaderText = (TextView) view.findViewById(R.id.headerText);
|
||||
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
|
||||
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
|
||||
mLockPatternView.setTactileFeedbackEnabled(
|
||||
mChooseLockSettingsHelper.utils().isTactileFeedbackEnabled());
|
||||
|
||||
mFooterText = (TextView) view.findViewById(R.id.footerText);
|
||||
|
||||
mFooterLeftButton = (TextView) view.findViewById(R.id.footerLeftButton);
|
||||
mFooterRightButton = (TextView) view.findViewById(R.id.footerRightButton);
|
||||
|
||||
mTitleHeaderScrollView = (ScrollView) view.findViewById(R.id
|
||||
.scroll_layout_title_header);
|
||||
|
||||
mFooterLeftButton.setOnClickListener(this);
|
||||
mFooterRightButton.setOnClickListener(this);
|
||||
|
||||
// make it so unhandled touch events within the unlock screen go to the
|
||||
// lock pattern view.
|
||||
final LinearLayoutWithDefaultTouchRecepient topLayout
|
||||
= (LinearLayoutWithDefaultTouchRecepient) view.findViewById(
|
||||
R.id.topLayout);
|
||||
topLayout.setDefaultTouchRecepient(mLockPatternView);
|
||||
|
||||
final boolean confirmCredentials = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
|
||||
Intent intent = getActivity().getIntent();
|
||||
mCurrentPattern = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
mHasChallenge = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (confirmCredentials) {
|
||||
// first launch. As a security measure, we're in NeedToConfirm mode until we
|
||||
// know there isn't an existing password or the user confirms their password.
|
||||
updateStage(Stage.NeedToConfirm);
|
||||
boolean launchedConfirmationActivity =
|
||||
mChooseLockSettingsHelper.launchConfirmationActivity(
|
||||
CONFIRM_EXISTING_REQUEST,
|
||||
getString(R.string.unlock_set_unlock_launch_picker_title), true,
|
||||
mUserId);
|
||||
if (!launchedConfirmationActivity) {
|
||||
updateStage(Stage.Introduction);
|
||||
}
|
||||
} else {
|
||||
updateStage(Stage.Introduction);
|
||||
}
|
||||
} else {
|
||||
// restore from previous state
|
||||
final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE);
|
||||
if (patternString != null) {
|
||||
mChosenPattern = LockPatternUtils.stringToPattern(patternString);
|
||||
}
|
||||
|
||||
if (mCurrentPattern == null) {
|
||||
mCurrentPattern = savedInstanceState.getString(KEY_CURRENT_PATTERN);
|
||||
}
|
||||
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
|
||||
|
||||
// Re-attach to the exiting worker if there is one.
|
||||
mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateStage(mUiStage);
|
||||
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
setRightButtonEnabled(false);
|
||||
mSaveAndFinishWorker.setListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
mSaveAndFinishWorker.setListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected Intent getRedactionInterstitialIntent(Context context) {
|
||||
return RedactionInterstitial.createStartIntent(context, mUserId);
|
||||
}
|
||||
|
||||
public void handleLeftButton() {
|
||||
if (mUiStage.leftMode == LeftButtonMode.Retry) {
|
||||
mChosenPattern = null;
|
||||
mLockPatternView.clearPattern();
|
||||
updateStage(Stage.Introduction);
|
||||
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
|
||||
getActivity().finish();
|
||||
} else {
|
||||
throw new IllegalStateException("left footer button pressed, but stage of " +
|
||||
mUiStage + " doesn't make sense");
|
||||
}
|
||||
}
|
||||
|
||||
public void handleRightButton() {
|
||||
if (mUiStage.rightMode == RightButtonMode.Continue) {
|
||||
if (mUiStage != Stage.FirstChoiceValid) {
|
||||
throw new IllegalStateException("expected ui stage "
|
||||
+ Stage.FirstChoiceValid + " when button is "
|
||||
+ RightButtonMode.Continue);
|
||||
}
|
||||
updateStage(Stage.NeedToConfirm);
|
||||
} else if (mUiStage.rightMode == RightButtonMode.Confirm) {
|
||||
if (mUiStage != Stage.ChoiceConfirmed) {
|
||||
throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
|
||||
+ " when button is " + RightButtonMode.Confirm);
|
||||
}
|
||||
startSaveAndFinish();
|
||||
} else if (mUiStage.rightMode == RightButtonMode.Ok) {
|
||||
if (mUiStage != Stage.HelpScreen) {
|
||||
throw new IllegalStateException("Help screen is only mode with ok button, "
|
||||
+ "but stage is " + mUiStage);
|
||||
}
|
||||
mLockPatternView.clearPattern();
|
||||
mLockPatternView.setDisplayMode(DisplayMode.Correct);
|
||||
updateStage(Stage.Introduction);
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v == mFooterLeftButton) {
|
||||
handleLeftButton();
|
||||
} else if (v == mFooterRightButton) {
|
||||
handleRightButton();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
if (mUiStage == Stage.HelpScreen) {
|
||||
updateStage(Stage.Introduction);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
|
||||
updateStage(Stage.HelpScreen);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
|
||||
if (mChosenPattern != null) {
|
||||
outState.putString(KEY_PATTERN_CHOICE,
|
||||
LockPatternUtils.patternToString(mChosenPattern));
|
||||
}
|
||||
|
||||
if (mCurrentPattern != null) {
|
||||
outState.putString(KEY_CURRENT_PATTERN,
|
||||
mCurrentPattern);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the messages and buttons appropriate to what stage the user
|
||||
* is at in choosing a view. This doesn't handle clearing out the pattern;
|
||||
* the pattern is expected to be in the right state.
|
||||
* @param stage
|
||||
*/
|
||||
protected void updateStage(Stage stage) {
|
||||
final Stage previousStage = mUiStage;
|
||||
|
||||
mUiStage = stage;
|
||||
|
||||
// header text, footer text, visibility and
|
||||
// enabled state all known from the stage
|
||||
if (stage == Stage.ChoiceTooShort) {
|
||||
mHeaderText.setText(
|
||||
getResources().getString(
|
||||
stage.headerMessage,
|
||||
LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
|
||||
} else {
|
||||
mHeaderText.setText(stage.headerMessage);
|
||||
}
|
||||
if (stage.footerMessage == ID_EMPTY_MESSAGE) {
|
||||
mFooterText.setText("");
|
||||
} else {
|
||||
mFooterText.setText(stage.footerMessage);
|
||||
}
|
||||
|
||||
if (stage.leftMode == LeftButtonMode.Gone) {
|
||||
mFooterLeftButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
mFooterLeftButton.setVisibility(View.VISIBLE);
|
||||
mFooterLeftButton.setText(stage.leftMode.text);
|
||||
mFooterLeftButton.setEnabled(stage.leftMode.enabled);
|
||||
}
|
||||
|
||||
setRightButtonText(stage.rightMode.text);
|
||||
setRightButtonEnabled(stage.rightMode.enabled);
|
||||
|
||||
// same for whether the pattern is enabled
|
||||
if (stage.patternEnabled) {
|
||||
mLockPatternView.enableInput();
|
||||
} else {
|
||||
mLockPatternView.disableInput();
|
||||
}
|
||||
|
||||
// the rest of the stuff varies enough that it is easier just to handle
|
||||
// on a case by case basis.
|
||||
mLockPatternView.setDisplayMode(DisplayMode.Correct);
|
||||
boolean announceAlways = false;
|
||||
|
||||
switch (mUiStage) {
|
||||
case Introduction:
|
||||
mLockPatternView.clearPattern();
|
||||
break;
|
||||
case HelpScreen:
|
||||
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
|
||||
break;
|
||||
case ChoiceTooShort:
|
||||
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
|
||||
postClearPatternRunnable();
|
||||
announceAlways = true;
|
||||
break;
|
||||
case FirstChoiceValid:
|
||||
break;
|
||||
case NeedToConfirm:
|
||||
mLockPatternView.clearPattern();
|
||||
break;
|
||||
case ConfirmWrong:
|
||||
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
|
||||
postClearPatternRunnable();
|
||||
announceAlways = true;
|
||||
break;
|
||||
case ChoiceConfirmed:
|
||||
break;
|
||||
}
|
||||
|
||||
// If the stage changed, announce the header for accessibility. This
|
||||
// is a no-op when accessibility is disabled.
|
||||
if (previousStage != stage || announceAlways) {
|
||||
mHeaderText.announceForAccessibility(mHeaderText.getText());
|
||||
}
|
||||
}
|
||||
|
||||
// clear the wrong pattern unless they have started a new one
|
||||
// already
|
||||
private void postClearPatternRunnable() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private void startSaveAndFinish() {
|
||||
if (mSaveAndFinishWorker != null) {
|
||||
Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker.");
|
||||
return;
|
||||
}
|
||||
|
||||
setRightButtonEnabled(false);
|
||||
|
||||
mSaveAndFinishWorker = new SaveAndFinishWorker();
|
||||
mSaveAndFinishWorker.setListener(this);
|
||||
|
||||
getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
|
||||
FRAGMENT_TAG_SAVE_AND_FINISH).commit();
|
||||
getFragmentManager().executePendingTransactions();
|
||||
|
||||
final boolean required = getActivity().getIntent().getBooleanExtra(
|
||||
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
|
||||
mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required,
|
||||
mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
|
||||
getActivity().setResult(RESULT_FINISHED, resultData);
|
||||
|
||||
if (!wasSecureBefore) {
|
||||
Intent intent = getRedactionInterstitialIntent(getActivity());
|
||||
if (intent != null) {
|
||||
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
|
||||
|
||||
private List<LockPatternView.Cell> mChosenPattern;
|
||||
private String mCurrentPattern;
|
||||
private boolean mLockVirgin;
|
||||
|
||||
public void start(LockPatternUtils utils, boolean credentialRequired,
|
||||
boolean hasChallenge, long challenge,
|
||||
List<LockPatternView.Cell> chosenPattern, String currentPattern, int userId) {
|
||||
prepare(utils, credentialRequired, hasChallenge, challenge, userId);
|
||||
|
||||
mCurrentPattern = currentPattern;
|
||||
mChosenPattern = chosenPattern;
|
||||
mUserId = userId;
|
||||
|
||||
mLockVirgin = !mUtils.isPatternEverChosen(mUserId);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent saveAndVerifyInBackground() {
|
||||
Intent result = null;
|
||||
final int userId = mUserId;
|
||||
mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId);
|
||||
|
||||
if (mHasChallenge) {
|
||||
byte[] token;
|
||||
try {
|
||||
token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId);
|
||||
} catch (RequestThrottledException e) {
|
||||
token = null;
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
Log.e(TAG, "critical: no token returned for known good pattern");
|
||||
}
|
||||
|
||||
result = new Intent();
|
||||
result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish(Intent resultData) {
|
||||
if (mLockVirgin) {
|
||||
mUtils.setVisiblePatternEnabled(true, mUserId);
|
||||
}
|
||||
|
||||
super.finish(resultData);
|
||||
}
|
||||
}
|
||||
}
|
||||
288
src/com/android/settings/password/ChooseLockSettingsHelper.java
Normal file
288
src/com/android/settings/password/ChooseLockSettingsHelper.java
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public final class ChooseLockSettingsHelper {
|
||||
|
||||
public static final String EXTRA_KEY_TYPE = "type";
|
||||
public static final String EXTRA_KEY_PASSWORD = "password";
|
||||
public static final String EXTRA_KEY_RETURN_CREDENTIALS = "return_credentials";
|
||||
public static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
|
||||
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
||||
public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
|
||||
public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
|
||||
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
|
||||
|
||||
|
||||
@VisibleForTesting LockPatternUtils mLockPatternUtils;
|
||||
private Activity mActivity;
|
||||
private Fragment mFragment;
|
||||
|
||||
public ChooseLockSettingsHelper(Activity activity) {
|
||||
mActivity = activity;
|
||||
mLockPatternUtils = new LockPatternUtils(activity);
|
||||
}
|
||||
|
||||
public ChooseLockSettingsHelper(Activity activity, Fragment fragment) {
|
||||
this(activity);
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
public LockPatternUtils utils() {
|
||||
return mLockPatternUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivity(int request, CharSequence title) {
|
||||
return launchConfirmationActivity(request, title, null, null, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
|
||||
* this can only be called internally.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials) {
|
||||
return launchConfirmationActivity(request, title, null, null, returnCredentials, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
|
||||
* this can only be called internally.
|
||||
* @param userId The userId for whom the lock should be confirmed.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivity(int request, CharSequence title,
|
||||
boolean returnCredentials, int userId) {
|
||||
return launchConfirmationActivity(request, title, null, null,
|
||||
returnCredentials, false, false, 0, Utils.enforceSameOwner(mActivity, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param header header of the confirmation screen; shown as large text
|
||||
* @param description description of the confirmation screen
|
||||
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
|
||||
* this can only be called internally.
|
||||
* @param external specifies whether this activity is launched externally, meaning that it will
|
||||
* get a dark theme, allow fingerprint authentication and it will forward
|
||||
* activity result.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||
boolean returnCredentials, boolean external) {
|
||||
return launchConfirmationActivity(request, title, header, description,
|
||||
returnCredentials, external, false, 0, Utils.getCredentialOwnerUserId(mActivity));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param header header of the confirmation screen; shown as large text
|
||||
* @param description description of the confirmation screen
|
||||
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
|
||||
* this can only be called internally.
|
||||
* @param external specifies whether this activity is launched externally, meaning that it will
|
||||
* get a dark theme, allow fingerprint authentication and it will forward
|
||||
* activity result.
|
||||
* @param userId The userId for whom the lock should be confirmed.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||
boolean returnCredentials, boolean external, int userId) {
|
||||
return launchConfirmationActivity(request, title, header, description,
|
||||
returnCredentials, external, false, 0, Utils.enforceSameOwner(mActivity, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param header header of the confirmation screen; shown as large text
|
||||
* @param description description of the confirmation screen
|
||||
* @param challenge a challenge to be verified against the device credential.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||
long challenge) {
|
||||
return launchConfirmationActivity(request, title, header, description,
|
||||
true, false, true, challenge, Utils.getCredentialOwnerUserId(mActivity));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param header header of the confirmation screen; shown as large text
|
||||
* @param description description of the confirmation screen
|
||||
* @param challenge a challenge to be verified against the device credential.
|
||||
* @param userId The userId for whom the lock should be confirmed.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||
long challenge, int userId) {
|
||||
return launchConfirmationActivity(request, title, header, description,
|
||||
true, false, true, challenge, Utils.enforceSameOwner(mActivity, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
|
||||
*
|
||||
* @param title title of the confirmation screen; shown in the action bar
|
||||
* @param header header of the confirmation screen; shown as large text
|
||||
* @param description description of the confirmation screen
|
||||
* @param external specifies whether this activity is launched externally, meaning that it will
|
||||
* get a dark theme, allow fingerprint authentication and it will forward
|
||||
* activity result.
|
||||
* @param challenge a challenge to be verified against the device credential.
|
||||
* @param userId The userId for whom the lock should be confirmed.
|
||||
* @return true if one exists and we launched an activity to confirm it
|
||||
* @see Activity#onActivityResult(int, int, android.content.Intent)
|
||||
*/
|
||||
public boolean launchConfirmationActivityWithExternalAndChallenge(int request,
|
||||
@Nullable CharSequence title, @Nullable CharSequence header,
|
||||
@Nullable CharSequence description, boolean external, long challenge, int userId) {
|
||||
return launchConfirmationActivity(request, title, header, description, false,
|
||||
external, true, challenge, Utils.enforceSameOwner(mActivity, userId));
|
||||
}
|
||||
|
||||
private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
|
||||
@Nullable CharSequence header, @Nullable CharSequence description,
|
||||
boolean returnCredentials, boolean external, boolean hasChallenge,
|
||||
long challenge, int userId) {
|
||||
final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId);
|
||||
boolean launched = false;
|
||||
|
||||
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) {
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||
launched = launchConfirmationActivity(request, title, header, description,
|
||||
returnCredentials || hasChallenge
|
||||
? ConfirmLockPattern.InternalActivity.class
|
||||
: ConfirmLockPattern.class, returnCredentials, external,
|
||||
hasChallenge, challenge, userId);
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||
launched = launchConfirmationActivity(request, title, header, description,
|
||||
returnCredentials || hasChallenge
|
||||
? ConfirmLockPassword.InternalActivity.class
|
||||
: ConfirmLockPassword.class, returnCredentials, external,
|
||||
hasChallenge, challenge, userId);
|
||||
break;
|
||||
}
|
||||
return launched;
|
||||
}
|
||||
|
||||
private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
|
||||
CharSequence message, Class<?> activityClass, boolean returnCredentials,
|
||||
boolean external, boolean hasChallenge, long challenge,
|
||||
int userId) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.ALLOW_FP_AUTHENTICATION, external);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, external);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, external);
|
||||
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
|
||||
// we should never have a drawer when confirming device credentials.
|
||||
intent.putExtra(SettingsActivity.EXTRA_HIDE_DRAWER, true);
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
|
||||
if (external) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
|
||||
if (mFragment != null) {
|
||||
copyOptionalExtras(mFragment.getActivity().getIntent(), intent);
|
||||
mFragment.startActivity(intent);
|
||||
} else {
|
||||
copyOptionalExtras(mActivity.getIntent(), intent);
|
||||
mActivity.startActivity(intent);
|
||||
}
|
||||
} else {
|
||||
if (mFragment != null) {
|
||||
mFragment.startActivityForResult(intent, request);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, request);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
|
||||
IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
if (intentSender != null) {
|
||||
outIntent.putExtra(Intent.EXTRA_INTENT, intentSender);
|
||||
}
|
||||
int taskId = inIntent.getIntExtra(Intent.EXTRA_TASK_ID, -1);
|
||||
if (taskId != -1) {
|
||||
outIntent.putExtra(Intent.EXTRA_TASK_ID, taskId);
|
||||
}
|
||||
// If we will launch another activity once credentials are confirmed, exclude from recents.
|
||||
// This is a workaround to a framework bug where affinity is incorrect for activities
|
||||
// that are started from a no display activity, as is ConfirmDeviceCredentialActivity.
|
||||
// TODO: Remove once that bug is fixed.
|
||||
if (intentSender != null || taskId != -1) {
|
||||
outIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
outIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 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.Activity;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
/**
|
||||
* Launch this when you want to confirm the user is present by asking them to enter their
|
||||
* PIN/password/pattern.
|
||||
*/
|
||||
public class ConfirmDeviceCredentialActivity extends Activity {
|
||||
public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
|
||||
|
||||
public static class InternalActivity extends ConfirmDeviceCredentialActivity {
|
||||
}
|
||||
|
||||
public static Intent createIntent(CharSequence title, CharSequence details) {
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.android.settings",
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
|
||||
intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent createIntent(CharSequence title, CharSequence details, long challenge) {
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.android.settings",
|
||||
ConfirmDeviceCredentialActivity.class.getName());
|
||||
intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
|
||||
intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String title = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
|
||||
String details = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
|
||||
int userId = Utils.getCredentialOwnerUserId(this);
|
||||
if (isInternalActivity()) {
|
||||
try {
|
||||
userId = Utils.getUserIdFromBundle(this, intent.getExtras());
|
||||
} catch (SecurityException se) {
|
||||
Log.e(TAG, "Invalid intent extra", se);
|
||||
}
|
||||
}
|
||||
final boolean isManagedProfile = UserManager.get(this).isManagedProfile(userId);
|
||||
// if the client app did not hand in a title and we are about to show the work challenge,
|
||||
// check whether there is a policy setting the organization name and use that as title
|
||||
if ((title == null) && isManagedProfile) {
|
||||
title = getTitleFromOrganizationName(userId);
|
||||
}
|
||||
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
|
||||
final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
|
||||
boolean launched;
|
||||
// If the target is a managed user and user key not unlocked yet, we will force unlock
|
||||
// tied profile so it will enable work mode and unlock managed profile, when personal
|
||||
// challenge is unlocked.
|
||||
if (isManagedProfile && isInternalActivity()
|
||||
&& !lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
|
||||
// We set the challenge as 0L, so it will force to unlock managed profile when it
|
||||
// unlocks primary profile screen lock, by calling verifyTiedProfileChallenge()
|
||||
launched = helper.launchConfirmationActivityWithExternalAndChallenge(
|
||||
0 /* request code */, null /* title */, title, details, true /* isExternal */,
|
||||
0L /* challenge */, userId);
|
||||
} else {
|
||||
launched = helper.launchConfirmationActivity(0 /* request code */, null /* title */,
|
||||
title, details, false /* returnCredentials */, true /* isExternal */, userId);
|
||||
}
|
||||
if (!launched) {
|
||||
Log.d(TAG, "No pattern, password or PIN set.");
|
||||
setResult(Activity.RESULT_OK);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
|
||||
}
|
||||
|
||||
private String getTitleFromOrganizationName(int userId) {
|
||||
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
|
||||
Context.DEVICE_POLICY_SERVICE);
|
||||
CharSequence organizationNameForUser = (dpm != null)
|
||||
? dpm.getOrganizationNameForUser(userId) : null;
|
||||
return organizationNameForUser != null ? organizationNameForUser.toString() : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.KeyguardManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.view.MenuItem;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
|
||||
|
||||
private static final String STATE_IS_KEYGUARD_LOCKED = "STATE_IS_KEYGUARD_LOCKED";
|
||||
|
||||
enum ConfirmCredentialTheme {
|
||||
INTERNAL,
|
||||
DARK,
|
||||
WORK
|
||||
}
|
||||
|
||||
private boolean mRestoring;
|
||||
private boolean mEnterAnimationPending;
|
||||
private boolean mFirstTimeVisible = true;
|
||||
private boolean mIsKeyguardLocked = false;
|
||||
private ConfirmCredentialTheme mConfirmCredentialTheme;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
int credentialOwnerUserId = Utils.getCredentialOwnerUserId(this,
|
||||
Utils.getUserIdFromBundle(this, getIntent().getExtras()));
|
||||
if (UserManager.get(this).isManagedProfile(credentialOwnerUserId)) {
|
||||
setTheme(R.style.Theme_ConfirmDeviceCredentialsWork);
|
||||
mConfirmCredentialTheme = ConfirmCredentialTheme.WORK;
|
||||
} else if (getIntent().getBooleanExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
|
||||
setTheme(R.style.Theme_ConfirmDeviceCredentialsDark);
|
||||
mConfirmCredentialTheme = ConfirmCredentialTheme.DARK;
|
||||
} else {
|
||||
setTheme(R.style.GlifTheme_Light);
|
||||
mConfirmCredentialTheme = ConfirmCredentialTheme.INTERNAL;
|
||||
}
|
||||
super.onCreate(savedState);
|
||||
|
||||
if (mConfirmCredentialTheme == ConfirmCredentialTheme.INTERNAL) {
|
||||
// Prevent the content parent from consuming the window insets because GlifLayout uses
|
||||
// it to show the status bar background.
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
mIsKeyguardLocked = savedState == null
|
||||
? getSystemService(KeyguardManager.class).isKeyguardLocked()
|
||||
: savedState.getBoolean(STATE_IS_KEYGUARD_LOCKED, false);
|
||||
// If the activity is launched, not due to config change, when keyguard is locked and the
|
||||
// flag is set, assume it's launched on top of keyguard on purpose.
|
||||
// TODO: Don't abuse SHOW_WHEN_LOCKED and don't check isKeyguardLocked.
|
||||
// Set extra SHOW_WHEN_LOCKED and WindowManager FLAG_SHOW_WHEN_LOCKED only if it's
|
||||
// truly on top of keyguard on purpose
|
||||
if (mIsKeyguardLocked && getIntent().getBooleanExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, false)) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
CharSequence msg = getIntent().getStringExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
|
||||
setTitle(msg);
|
||||
if (getActionBar() != null) {
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
mRestoring = savedState != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(STATE_IS_KEYGUARD_LOCKED, mIsKeyguardLocked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!isChangingConfigurations() && !mRestoring
|
||||
&& mConfirmCredentialTheme == ConfirmCredentialTheme.DARK && mFirstTimeVisible) {
|
||||
mFirstTimeVisible = false;
|
||||
prepareEnterAnimation();
|
||||
mEnterAnimationPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfirmDeviceCredentialBaseFragment getFragment() {
|
||||
Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
|
||||
if (fragment != null && fragment instanceof ConfirmDeviceCredentialBaseFragment) {
|
||||
return (ConfirmDeviceCredentialBaseFragment) fragment;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnterAnimationComplete() {
|
||||
super.onEnterAnimationComplete();
|
||||
if (mEnterAnimationPending) {
|
||||
startEnterAnimation();
|
||||
mEnterAnimationPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void prepareEnterAnimation() {
|
||||
getFragment().prepareEnterAnimation();
|
||||
}
|
||||
|
||||
public void startEnterAnimation() {
|
||||
getFragment().startEnterAnimation();
|
||||
}
|
||||
|
||||
public ConfirmCredentialTheme getConfirmCredentialTheme() {
|
||||
return mConfirmCredentialTheme;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
// TODO (b/35202196): move this class out of the root of the package.
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.OptionsMenuFragment;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.fingerprint.FingerprintUiHelper;
|
||||
|
||||
/**
|
||||
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
|
||||
*/
|
||||
public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFragment
|
||||
implements FingerprintUiHelper.Callback {
|
||||
|
||||
public static final String PACKAGE = "com.android.settings";
|
||||
public static final String TITLE_TEXT = PACKAGE + ".ConfirmCredentials.title";
|
||||
public static final String HEADER_TEXT = PACKAGE + ".ConfirmCredentials.header";
|
||||
public static final String DETAILS_TEXT = PACKAGE + ".ConfirmCredentials.details";
|
||||
public static final String ALLOW_FP_AUTHENTICATION =
|
||||
PACKAGE + ".ConfirmCredentials.allowFpAuthentication";
|
||||
public static final String DARK_THEME = PACKAGE + ".ConfirmCredentials.darkTheme";
|
||||
public static final String SHOW_CANCEL_BUTTON =
|
||||
PACKAGE + ".ConfirmCredentials.showCancelButton";
|
||||
public static final String SHOW_WHEN_LOCKED =
|
||||
PACKAGE + ".ConfirmCredentials.showWhenLocked";
|
||||
|
||||
private FingerprintUiHelper mFingerprintHelper;
|
||||
protected boolean mReturnCredentials = false;
|
||||
protected Button mCancelButton;
|
||||
protected ImageView mFingerprintIcon;
|
||||
protected int mEffectiveUserId;
|
||||
protected int mUserId;
|
||||
protected UserManager mUserManager;
|
||||
protected LockPatternUtils mLockPatternUtils;
|
||||
protected TextView mErrorTextView;
|
||||
protected final Handler mHandler = new Handler();
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mReturnCredentials = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false);
|
||||
// Only take this argument into account if it belongs to the current profile.
|
||||
Intent intent = getActivity().getIntent();
|
||||
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
|
||||
mUserManager = UserManager.get(getActivity());
|
||||
mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
|
||||
mLockPatternUtils = new LockPatternUtils(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
|
||||
mFingerprintIcon = (ImageView) view.findViewById(R.id.fingerprintIcon);
|
||||
mFingerprintHelper = new FingerprintUiHelper(
|
||||
mFingerprintIcon,
|
||||
(TextView) view.findViewById(R.id.errorText), this, mEffectiveUserId);
|
||||
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
|
||||
SHOW_CANCEL_BUTTON, false);
|
||||
mCancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE);
|
||||
mCancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
getActivity().finish();
|
||||
}
|
||||
});
|
||||
int credentialOwnerUserId = Utils.getCredentialOwnerUserId(
|
||||
getActivity(),
|
||||
Utils.getUserIdFromBundle(
|
||||
getActivity(),
|
||||
getActivity().getIntent().getExtras()));
|
||||
if (mUserManager.isManagedProfile(credentialOwnerUserId)) {
|
||||
setWorkChallengeBackground(view, credentialOwnerUserId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFingerprintDisabledByAdmin() {
|
||||
DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService(
|
||||
Context.DEVICE_POLICY_SERVICE);
|
||||
final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, mEffectiveUserId);
|
||||
return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
|
||||
}
|
||||
|
||||
// User could be locked while Effective user is unlocked even though the effective owns the
|
||||
// credential. Otherwise, fingerprint can't unlock fbe/keystore through
|
||||
// verifyTiedProfileChallenge. In such case, we also wanna show the user message that
|
||||
// fingerprint is disabled due to device restart.
|
||||
protected boolean isFingerprintDisallowedByStrongAuth() {
|
||||
return !(mLockPatternUtils.isFingerprintAllowedForUser(mEffectiveUserId)
|
||||
&& mUserManager.isUserUnlocked(mUserId));
|
||||
}
|
||||
|
||||
private boolean isFingerprintAllowed() {
|
||||
return !mReturnCredentials
|
||||
&& getActivity().getIntent().getBooleanExtra(ALLOW_FP_AUTHENTICATION, false)
|
||||
&& !isFingerprintDisallowedByStrongAuth()
|
||||
&& !isFingerprintDisabledByAdmin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refreshLockScreen();
|
||||
}
|
||||
|
||||
protected void refreshLockScreen() {
|
||||
if (isFingerprintAllowed()) {
|
||||
mFingerprintHelper.startListening();
|
||||
} else {
|
||||
if (mFingerprintHelper.isListening()) {
|
||||
mFingerprintHelper.stopListening();
|
||||
}
|
||||
}
|
||||
if (isProfileChallenge()) {
|
||||
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
|
||||
mEffectiveUserId));
|
||||
}
|
||||
}
|
||||
|
||||
protected void setAccessibilityTitle(CharSequence supplementalText) {
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (intent != null) {
|
||||
CharSequence titleText = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
|
||||
if (supplementalText == null) {
|
||||
return;
|
||||
}
|
||||
if (titleText == null) {
|
||||
getActivity().setTitle(supplementalText);
|
||||
} else {
|
||||
String accessibilityTitle =
|
||||
new StringBuilder(titleText).append(",").append(supplementalText).toString();
|
||||
getActivity().setTitle(Utils.createAccessibleSequence(titleText, accessibilityTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (mFingerprintHelper.isListening()) {
|
||||
mFingerprintHelper.stopListening();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
// Check whether we are still active.
|
||||
if (getActivity() != null && getActivity().isResumed()) {
|
||||
TrustManager trustManager =
|
||||
(TrustManager) getActivity().getSystemService(Context.TRUST_SERVICE);
|
||||
trustManager.setDeviceLockedForUser(mEffectiveUserId, false);
|
||||
authenticationSucceeded();
|
||||
checkForPendingIntent();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void authenticationSucceeded();
|
||||
|
||||
@Override
|
||||
public void onFingerprintIconVisibilityChanged(boolean visible) {
|
||||
}
|
||||
|
||||
public void prepareEnterAnimation() {
|
||||
}
|
||||
|
||||
public void startEnterAnimation() {
|
||||
}
|
||||
|
||||
protected void checkForPendingIntent() {
|
||||
int taskId = getActivity().getIntent().getIntExtra(Intent.EXTRA_TASK_ID, -1);
|
||||
if (taskId != -1) {
|
||||
try {
|
||||
IActivityManager activityManager = ActivityManager.getService();
|
||||
final ActivityOptions options = ActivityOptions.makeBasic();
|
||||
options.setLaunchStackId(ActivityManager.StackId.INVALID_STACK_ID);
|
||||
activityManager.startActivityFromRecents(taskId, options.toBundle());
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
IntentSender intentSender = getActivity().getIntent()
|
||||
.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
if (intentSender != null) {
|
||||
try {
|
||||
getActivity().startIntentSenderForResult(intentSender, -1, null, 0, 0, 0);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setWorkChallengeBackground(View baseView, int userId) {
|
||||
View mainContent = getActivity().findViewById(com.android.settings.R.id.main_content);
|
||||
if (mainContent != null) {
|
||||
// Remove the main content padding so that the background image is full screen.
|
||||
mainContent.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService(
|
||||
Context.DEVICE_POLICY_SERVICE);
|
||||
baseView.setBackground(new ColorDrawable(dpm.getOrganizationColorForUser(userId)));
|
||||
ImageView imageView = (ImageView) baseView.findViewById(R.id.background_image);
|
||||
if (imageView != null) {
|
||||
Drawable image = getResources().getDrawable(R.drawable.work_challenge_background);
|
||||
image.setColorFilter(
|
||||
getResources().getColor(R.color.confirm_device_credential_transparent_black),
|
||||
PorterDuff.Mode.DARKEN);
|
||||
imageView.setImageDrawable(image);
|
||||
Point screenSize = new Point();
|
||||
getActivity().getWindowManager().getDefaultDisplay().getSize(screenSize);
|
||||
imageView.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
screenSize.y));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isProfileChallenge() {
|
||||
return mUserManager.isManagedProfile(mEffectiveUserId);
|
||||
}
|
||||
|
||||
protected void reportSuccessfullAttempt() {
|
||||
if (isProfileChallenge()) {
|
||||
mLockPatternUtils.reportSuccessfulPasswordAttempt(mEffectiveUserId);
|
||||
// Keyguard is responsible to disable StrongAuth for primary user. Disable StrongAuth
|
||||
// for work challenge only here.
|
||||
mLockPatternUtils.userPresent(mEffectiveUserId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void reportFailedAttempt() {
|
||||
if (isProfileChallenge()) {
|
||||
// + 1 for this attempt.
|
||||
updateErrorMessage(
|
||||
mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
|
||||
mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateErrorMessage(int numAttempts) {
|
||||
final int maxAttempts =
|
||||
mLockPatternUtils.getMaximumFailedPasswordsForWipe(mEffectiveUserId);
|
||||
if (maxAttempts > 0 && numAttempts > 0) {
|
||||
int remainingAttempts = maxAttempts - numAttempts;
|
||||
if (remainingAttempts == 1) {
|
||||
// Last try
|
||||
final String title = getActivity().getString(
|
||||
R.string.lock_profile_wipe_warning_title);
|
||||
LastTryDialog.show(getFragmentManager(), title, getLastTryErrorMessage(),
|
||||
android.R.string.ok, false /* dismiss */);
|
||||
} else if (remainingAttempts <= 0) {
|
||||
// Profile is wiped
|
||||
LastTryDialog.show(getFragmentManager(), null /* title */,
|
||||
R.string.lock_profile_wipe_content, R.string.lock_profile_wipe_dismiss,
|
||||
true /* dismiss */);
|
||||
}
|
||||
if (mErrorTextView != null) {
|
||||
final String message = getActivity().getString(R.string.lock_profile_wipe_attempts,
|
||||
numAttempts, maxAttempts);
|
||||
showError(message, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int getLastTryErrorMessage();
|
||||
|
||||
private final Runnable mResetErrorRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mErrorTextView.setText("");
|
||||
}
|
||||
};
|
||||
|
||||
protected void showError(CharSequence msg, long timeout) {
|
||||
mErrorTextView.setText(msg);
|
||||
onShowError();
|
||||
mHandler.removeCallbacks(mResetErrorRunnable);
|
||||
if (timeout != 0) {
|
||||
mHandler.postDelayed(mResetErrorRunnable, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onShowError();
|
||||
|
||||
protected void showError(int msg, long timeout) {
|
||||
showError(getText(msg), timeout);
|
||||
}
|
||||
|
||||
public static class LastTryDialog extends DialogFragment {
|
||||
private static final String TAG = LastTryDialog.class.getSimpleName();
|
||||
|
||||
private static final String ARG_TITLE = "title";
|
||||
private static final String ARG_MESSAGE = "message";
|
||||
private static final String ARG_BUTTON = "button";
|
||||
private static final String ARG_DISMISS = "dismiss";
|
||||
|
||||
static boolean show(FragmentManager from, String title, int message, int button,
|
||||
boolean dismiss) {
|
||||
LastTryDialog existent = (LastTryDialog) from.findFragmentByTag(TAG);
|
||||
if (existent != null && !existent.isRemoving()) {
|
||||
return false;
|
||||
}
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_TITLE, title);
|
||||
args.putInt(ARG_MESSAGE, message);
|
||||
args.putInt(ARG_BUTTON, button);
|
||||
args.putBoolean(ARG_DISMISS, dismiss);
|
||||
|
||||
DialogFragment dialog = new LastTryDialog();
|
||||
dialog.setArguments(args);
|
||||
dialog.show(from, TAG);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hide(FragmentManager from) {
|
||||
LastTryDialog dialog = (LastTryDialog) from.findFragmentByTag(TAG);
|
||||
if (dialog != null) {
|
||||
dialog.dismissAllowingStateLoss();
|
||||
from.executePendingTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog setup.
|
||||
* <p>
|
||||
* To make it less likely that the dialog is dismissed accidentally, for example if the
|
||||
* device is malfunctioning or if the device is in a pocket, we set
|
||||
* {@code setCanceledOnTouchOutside(false)}.
|
||||
*/
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(getArguments().getString(ARG_TITLE))
|
||||
.setMessage(getArguments().getInt(ARG_MESSAGE))
|
||||
.setPositiveButton(getArguments().getInt(ARG_BUTTON), null)
|
||||
.create();
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(final DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if (getActivity() != null && getArguments().getBoolean(ARG_DISMISS)) {
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
529
src/com/android/settings/password/ConfirmLockPassword.java
Normal file
529
src/com/android/settings/password/ConfirmLockPassword.java
Normal file
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settingslib.animation.AppearAnimationUtils;
|
||||
import com.android.settingslib.animation.DisappearAnimationUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
// The index of the array is isStrongAuth << 2 + isProfile << 1 + isAlpha.
|
||||
private static final int[] DETAIL_TEXTS = new int[] {
|
||||
R.string.lockpassword_confirm_your_pin_generic,
|
||||
R.string.lockpassword_confirm_your_password_generic,
|
||||
R.string.lockpassword_confirm_your_pin_generic_profile,
|
||||
R.string.lockpassword_confirm_your_password_generic_profile,
|
||||
R.string.lockpassword_strong_auth_required_reason_restart_device_pin,
|
||||
R.string.lockpassword_strong_auth_required_reason_restart_device_password,
|
||||
R.string.lockpassword_strong_auth_required_reason_restart_work_pin,
|
||||
R.string.lockpassword_strong_auth_required_reason_restart_work_password,
|
||||
};
|
||||
|
||||
public static class InternalActivity extends ConfirmLockPassword {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
|
||||
if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
|
||||
((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
|
||||
implements OnClickListener, OnEditorActionListener,
|
||||
CredentialCheckResultTracker.Listener {
|
||||
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
|
||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||
private TextView mPasswordEntry;
|
||||
private TextViewInputDisabler mPasswordEntryInputDisabler;
|
||||
private AsyncTask<?, ?, ?> mPendingLockCheck;
|
||||
private CredentialCheckResultTracker mCredentialCheckResultTracker;
|
||||
private boolean mDisappearing = false;
|
||||
private TextView mHeaderTextView;
|
||||
private TextView mDetailsTextView;
|
||||
private CountDownTimer mCountdownTimer;
|
||||
private boolean mIsAlpha;
|
||||
private InputMethodManager mImm;
|
||||
private boolean mUsingFingerprint = false;
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
private boolean mBlockImm;
|
||||
|
||||
// required constructor for fragments
|
||||
public ConfirmLockPasswordFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
|
||||
mEffectiveUserId);
|
||||
|
||||
ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
|
||||
View view = inflater.inflate(
|
||||
activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.INTERNAL
|
||||
? R.layout.confirm_lock_password_internal
|
||||
: R.layout.confirm_lock_password,
|
||||
container,
|
||||
false);
|
||||
|
||||
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
|
||||
mPasswordEntry.setOnEditorActionListener(this);
|
||||
mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
|
||||
|
||||
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
|
||||
if (mHeaderTextView == null) {
|
||||
mHeaderTextView = view.findViewById(R.id.suw_layout_title);
|
||||
}
|
||||
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
|
||||
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
|
||||
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|
||||
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|
||||
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
|
||||
|| DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
|
||||
|
||||
mImm = (InputMethodManager) getActivity().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (intent != null) {
|
||||
CharSequence headerMessage = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
|
||||
CharSequence detailsMessage = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
||||
if (TextUtils.isEmpty(headerMessage)) {
|
||||
headerMessage = getString(getDefaultHeader());
|
||||
}
|
||||
if (TextUtils.isEmpty(detailsMessage)) {
|
||||
detailsMessage = getString(getDefaultDetails());
|
||||
}
|
||||
mHeaderTextView.setText(headerMessage);
|
||||
mDetailsTextView.setText(detailsMessage);
|
||||
}
|
||||
int currentType = mPasswordEntry.getInputType();
|
||||
mPasswordEntry.setInputType(mIsAlpha ? currentType
|
||||
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
|
||||
mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
|
||||
220, 2f /* translationScale */, 1f /* delayScale*/,
|
||||
AnimationUtils.loadInterpolator(getContext(),
|
||||
android.R.interpolator.linear_out_slow_in));
|
||||
mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
|
||||
110, 1f /* translationScale */,
|
||||
0.5f /* delayScale */, AnimationUtils.loadInterpolator(
|
||||
getContext(), android.R.interpolator.fast_out_linear_in));
|
||||
setAccessibilityTitle(mHeaderTextView.getText());
|
||||
|
||||
mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
|
||||
.findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
|
||||
if (mCredentialCheckResultTracker == null) {
|
||||
mCredentialCheckResultTracker = new CredentialCheckResultTracker();
|
||||
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
|
||||
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private int getDefaultHeader() {
|
||||
return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
|
||||
: R.string.lockpassword_confirm_your_pin_header;
|
||||
}
|
||||
|
||||
private int getDefaultDetails() {
|
||||
boolean isStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
|
||||
boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
|
||||
// Map boolean flags to an index by isStrongAuth << 2 + isProfile << 1 + isAlpha.
|
||||
int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((isProfile ? 1 : 0) << 1)
|
||||
+ (mIsAlpha ? 1 : 0);
|
||||
return DETAIL_TEXTS[index];
|
||||
}
|
||||
|
||||
private int getErrorMessage() {
|
||||
return mIsAlpha ? R.string.lockpassword_invalid_password
|
||||
: R.string.lockpassword_invalid_pin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLastTryErrorMessage() {
|
||||
return mIsAlpha ? R.string.lock_profile_wipe_warning_content_password
|
||||
: R.string.lock_profile_wipe_warning_content_pin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareEnterAnimation() {
|
||||
super.prepareEnterAnimation();
|
||||
mHeaderTextView.setAlpha(0f);
|
||||
mDetailsTextView.setAlpha(0f);
|
||||
mCancelButton.setAlpha(0f);
|
||||
mPasswordEntry.setAlpha(0f);
|
||||
mFingerprintIcon.setAlpha(0f);
|
||||
mBlockImm = true;
|
||||
}
|
||||
|
||||
private View[] getActiveViews() {
|
||||
ArrayList<View> result = new ArrayList<>();
|
||||
result.add(mHeaderTextView);
|
||||
result.add(mDetailsTextView);
|
||||
if (mCancelButton.getVisibility() == View.VISIBLE) {
|
||||
result.add(mCancelButton);
|
||||
}
|
||||
result.add(mPasswordEntry);
|
||||
if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
|
||||
result.add(mFingerprintIcon);
|
||||
}
|
||||
return result.toArray(new View[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startEnterAnimation() {
|
||||
super.startEnterAnimation();
|
||||
mAppearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mBlockImm = false;
|
||||
resetState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (mCountdownTimer != null) {
|
||||
mCountdownTimer.cancel();
|
||||
mCountdownTimer = null;
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.CONFIRM_LOCK_PASSWORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
|
||||
if (deadline != 0) {
|
||||
mCredentialCheckResultTracker.clearResult();
|
||||
handleAttemptLockout(deadline);
|
||||
} else {
|
||||
resetState();
|
||||
mErrorTextView.setText("");
|
||||
if (isProfileChallenge()) {
|
||||
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
|
||||
mEffectiveUserId));
|
||||
}
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void authenticationSucceeded() {
|
||||
mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFingerprintIconVisibilityChanged(boolean visible) {
|
||||
mUsingFingerprint = visible;
|
||||
}
|
||||
|
||||
private void resetState() {
|
||||
if (mBlockImm) return;
|
||||
mPasswordEntry.setEnabled(true);
|
||||
mPasswordEntryInputDisabler.setInputEnabled(true);
|
||||
if (shouldAutoShowSoftKeyboard()) {
|
||||
mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldAutoShowSoftKeyboard() {
|
||||
return mPasswordEntry.isEnabled() && !mUsingFingerprint;
|
||||
}
|
||||
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
if (!hasFocus || mBlockImm) {
|
||||
return;
|
||||
}
|
||||
// Post to let window focus logic to finish to allow soft input show/hide properly.
|
||||
mPasswordEntry.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (shouldAutoShowSoftKeyboard()) {
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
|
||||
mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(),
|
||||
InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleNext() {
|
||||
if (mPendingLockCheck != null || mDisappearing) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String pin = mPasswordEntry.getText().toString();
|
||||
if (TextUtils.isEmpty(pin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPasswordEntryInputDisabler.setInputEnabled(false);
|
||||
final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
|
||||
Intent intent = new Intent();
|
||||
if (verifyChallenge) {
|
||||
if (isInternalActivity()) {
|
||||
startVerifyPassword(pin, intent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startCheckPassword(pin, intent);
|
||||
return;
|
||||
}
|
||||
|
||||
mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
|
||||
}
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return getActivity() instanceof ConfirmLockPassword.InternalActivity;
|
||||
}
|
||||
|
||||
private void startVerifyPassword(final String pin, final Intent intent) {
|
||||
long challenge = getActivity().getIntent().getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
final int localUserId = mUserId;
|
||||
final LockPatternChecker.OnVerifyCallback onVerifyCallback =
|
||||
new LockPatternChecker.OnVerifyCallback() {
|
||||
@Override
|
||||
public void onVerified(byte[] token, int timeoutMs) {
|
||||
mPendingLockCheck = null;
|
||||
boolean matched = false;
|
||||
if (token != null) {
|
||||
matched = true;
|
||||
if (mReturnCredentials) {
|
||||
intent.putExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
||||
token);
|
||||
}
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localUserId);
|
||||
}
|
||||
};
|
||||
mPendingLockCheck = (localEffectiveUserId == localUserId)
|
||||
? LockPatternChecker.verifyPassword(
|
||||
mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
|
||||
: LockPatternChecker.verifyTiedProfileChallenge(
|
||||
mLockPatternUtils, pin, false, challenge, localUserId,
|
||||
onVerifyCallback);
|
||||
}
|
||||
|
||||
private void startCheckPassword(final String pin, final Intent intent) {
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
mPendingLockCheck = LockPatternChecker.checkPassword(
|
||||
mLockPatternUtils,
|
||||
pin,
|
||||
localEffectiveUserId,
|
||||
new LockPatternChecker.OnCheckCallback() {
|
||||
@Override
|
||||
public void onChecked(boolean matched, int timeoutMs) {
|
||||
mPendingLockCheck = null;
|
||||
if (matched && isInternalActivity() && mReturnCredentials) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
|
||||
mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
|
||||
: StorageManager.CRYPT_TYPE_PIN);
|
||||
intent.putExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localEffectiveUserId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startDisappearAnimation(final Intent intent) {
|
||||
if (mDisappearing) {
|
||||
return;
|
||||
}
|
||||
mDisappearing = true;
|
||||
|
||||
final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
|
||||
// Bail if there is no active activity.
|
||||
if (activity == null || activity.isFinishing()) {
|
||||
return;
|
||||
}
|
||||
if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
|
||||
mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
activity.overridePendingTransition(
|
||||
R.anim.confirm_credential_close_enter,
|
||||
R.anim.confirm_credential_close_exit);
|
||||
});
|
||||
} else {
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
mPasswordEntryInputDisabler.setInputEnabled(true);
|
||||
if (matched) {
|
||||
if (newResult) {
|
||||
reportSuccessfullAttempt();
|
||||
}
|
||||
startDisappearAnimation(intent);
|
||||
checkForPendingIntent();
|
||||
} else {
|
||||
if (timeoutMs > 0) {
|
||||
refreshLockScreen();
|
||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
||||
effectiveUserId, timeoutMs);
|
||||
handleAttemptLockout(deadline);
|
||||
} else {
|
||||
showError(getErrorMessage(), ERROR_MESSAGE_TIMEOUT);
|
||||
}
|
||||
if (newResult) {
|
||||
reportFailedAttempt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShowError() {
|
||||
mPasswordEntry.setText(null);
|
||||
}
|
||||
|
||||
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
|
||||
long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
mPasswordEntry.setEnabled(false);
|
||||
mCountdownTimer = new CountDownTimer(
|
||||
elapsedRealtimeDeadline - elapsedRealtime,
|
||||
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
|
||||
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
final int secondsCountdown = (int) (millisUntilFinished / 1000);
|
||||
showError(getString(
|
||||
R.string.lockpattern_too_many_failed_confirmation_attempts,
|
||||
secondsCountdown), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
resetState();
|
||||
mErrorTextView.setText("");
|
||||
if (isProfileChallenge()) {
|
||||
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
|
||||
mEffectiveUserId));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.next_button:
|
||||
handleNext();
|
||||
break;
|
||||
|
||||
case R.id.cancel_button:
|
||||
getActivity().setResult(RESULT_CANCELED);
|
||||
getActivity().finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// {@link OnEditorActionListener} methods.
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// Check if this was the result of hitting the enter or "done" key
|
||||
if (actionId == EditorInfo.IME_NULL
|
||||
|| actionId == EditorInfo.IME_ACTION_DONE
|
||||
|| actionId == EditorInfo.IME_ACTION_NEXT) {
|
||||
handleNext();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
572
src/com/android/settings/password/ConfirmLockPattern.java
Normal file
572
src/com/android/settings/password/ConfirmLockPattern.java
Normal file
@@ -0,0 +1,572 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.internal.widget.LockPatternView.Cell;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settingslib.animation.AppearAnimationCreator;
|
||||
import com.android.settingslib.animation.AppearAnimationUtils;
|
||||
import com.android.settingslib.animation.DisappearAnimationUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Launch this when you want the user to confirm their lock pattern.
|
||||
*
|
||||
* Sets an activity result of {@link Activity#RESULT_OK} when the user
|
||||
* successfully confirmed their pattern.
|
||||
*/
|
||||
public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
|
||||
public static class InternalActivity extends ConfirmLockPattern {
|
||||
}
|
||||
|
||||
private enum Stage {
|
||||
NeedToUnlock,
|
||||
NeedToUnlockWrong,
|
||||
LockedOut
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
Intent modIntent = new Intent(super.getIntent());
|
||||
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
|
||||
return modIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
if (ConfirmLockPatternFragment.class.getName().equals(fragmentName)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
|
||||
implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener {
|
||||
|
||||
// how long we wait to clear a wrong pattern
|
||||
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
|
||||
|
||||
private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
|
||||
|
||||
private LockPatternView mLockPatternView;
|
||||
private AsyncTask<?, ?, ?> mPendingLockCheck;
|
||||
private CredentialCheckResultTracker mCredentialCheckResultTracker;
|
||||
private boolean mDisappearing = false;
|
||||
private CountDownTimer mCountdownTimer;
|
||||
|
||||
private TextView mHeaderTextView;
|
||||
private TextView mDetailsTextView;
|
||||
private View mLeftSpacerLandscape;
|
||||
private View mRightSpacerLandscape;
|
||||
|
||||
// caller-supplied text for various prompts
|
||||
private CharSequence mHeaderText;
|
||||
private CharSequence mDetailsText;
|
||||
|
||||
private AppearAnimationUtils mAppearAnimationUtils;
|
||||
private DisappearAnimationUtils mDisappearAnimationUtils;
|
||||
|
||||
// required constructor for fragments
|
||||
public ConfirmLockPatternFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
|
||||
View view = inflater.inflate(
|
||||
activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.INTERNAL
|
||||
? R.layout.confirm_lock_pattern_internal
|
||||
: R.layout.confirm_lock_pattern,
|
||||
container,
|
||||
false);
|
||||
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
|
||||
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
|
||||
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
|
||||
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
|
||||
mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
|
||||
mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
|
||||
|
||||
// make it so unhandled touch events within the unlock screen go to the
|
||||
// lock pattern view.
|
||||
final LinearLayoutWithDefaultTouchRecepient topLayout
|
||||
= (LinearLayoutWithDefaultTouchRecepient) view.findViewById(R.id.topLayout);
|
||||
topLayout.setDefaultTouchRecepient(mLockPatternView);
|
||||
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (intent != null) {
|
||||
mHeaderText = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
|
||||
mDetailsText = intent.getCharSequenceExtra(
|
||||
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
|
||||
}
|
||||
|
||||
mLockPatternView.setTactileFeedbackEnabled(
|
||||
mLockPatternUtils.isTactileFeedbackEnabled());
|
||||
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
|
||||
mEffectiveUserId));
|
||||
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
|
||||
updateStage(Stage.NeedToUnlock);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// on first launch, if no lock pattern is set, then finish with
|
||||
// success (don't want user to get stuck confirming something that
|
||||
// doesn't exist).
|
||||
if (!mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
|
||||
getActivity().setResult(Activity.RESULT_OK);
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
|
||||
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 2f /* translationScale */,
|
||||
1.3f /* delayScale */, AnimationUtils.loadInterpolator(
|
||||
getContext(), android.R.interpolator.linear_out_slow_in));
|
||||
mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
|
||||
125, 4f /* translationScale */,
|
||||
0.3f /* delayScale */, AnimationUtils.loadInterpolator(
|
||||
getContext(), android.R.interpolator.fast_out_linear_in),
|
||||
new AppearAnimationUtils.RowTranslationScaler() {
|
||||
@Override
|
||||
public float getRowTranslationScale(int row, int numRows) {
|
||||
return (float)(numRows - row) / numRows;
|
||||
}
|
||||
});
|
||||
setAccessibilityTitle(mHeaderTextView.getText());
|
||||
|
||||
mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
|
||||
.findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
|
||||
if (mCredentialCheckResultTracker == null) {
|
||||
mCredentialCheckResultTracker = new CredentialCheckResultTracker();
|
||||
getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
|
||||
FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
// deliberately not calling super since we are managing this in full
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mCountdownTimer != null) {
|
||||
mCountdownTimer.cancel();
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.CONFIRM_LOCK_PATTERN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// if the user is currently locked out, enforce it.
|
||||
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
|
||||
if (deadline != 0) {
|
||||
mCredentialCheckResultTracker.clearResult();
|
||||
handleAttemptLockout(deadline);
|
||||
} else if (!mLockPatternView.isEnabled()) {
|
||||
// The deadline has passed, but the timer was cancelled. Or the pending lock
|
||||
// check was cancelled. Need to clean up.
|
||||
updateStage(Stage.NeedToUnlock);
|
||||
}
|
||||
mCredentialCheckResultTracker.setListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShowError() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareEnterAnimation() {
|
||||
super.prepareEnterAnimation();
|
||||
mHeaderTextView.setAlpha(0f);
|
||||
mCancelButton.setAlpha(0f);
|
||||
mLockPatternView.setAlpha(0f);
|
||||
mDetailsTextView.setAlpha(0f);
|
||||
mFingerprintIcon.setAlpha(0f);
|
||||
}
|
||||
|
||||
private int getDefaultDetails() {
|
||||
boolean isStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
|
||||
if (UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId)) {
|
||||
return isStrongAuthRequired
|
||||
? R.string.lockpassword_strong_auth_required_reason_restart_work_pattern
|
||||
: R.string.lockpassword_confirm_your_pattern_generic_profile;
|
||||
} else {
|
||||
return isStrongAuthRequired
|
||||
? R.string.lockpassword_strong_auth_required_reason_restart_device_pattern
|
||||
: R.string.lockpassword_confirm_your_pattern_generic;
|
||||
}
|
||||
}
|
||||
|
||||
private Object[][] getActiveViews() {
|
||||
ArrayList<ArrayList<Object>> result = new ArrayList<>();
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mHeaderTextView)));
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mDetailsTextView)));
|
||||
if (mCancelButton.getVisibility() == View.VISIBLE) {
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mCancelButton)));
|
||||
}
|
||||
LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
|
||||
for (int i = 0; i < cellStates.length; i++) {
|
||||
ArrayList<Object> row = new ArrayList<>();
|
||||
for (int j = 0; j < cellStates[i].length; j++) {
|
||||
row.add(cellStates[i][j]);
|
||||
}
|
||||
result.add(row);
|
||||
}
|
||||
if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
|
||||
result.add(new ArrayList<Object>(Collections.singletonList(mFingerprintIcon)));
|
||||
}
|
||||
Object[][] resultArr = new Object[result.size()][cellStates[0].length];
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
ArrayList<Object> row = result.get(i);
|
||||
for (int j = 0; j < row.size(); j++) {
|
||||
resultArr[i][j] = row.get(j);
|
||||
}
|
||||
}
|
||||
return resultArr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startEnterAnimation() {
|
||||
super.startEnterAnimation();
|
||||
mLockPatternView.setAlpha(1f);
|
||||
mAppearAnimationUtils.startAnimation2d(getActiveViews(), null, this);
|
||||
}
|
||||
|
||||
private void updateStage(Stage stage) {
|
||||
switch (stage) {
|
||||
case NeedToUnlock:
|
||||
if (mHeaderText != null) {
|
||||
mHeaderTextView.setText(mHeaderText);
|
||||
} else {
|
||||
mHeaderTextView.setText(R.string.lockpassword_confirm_your_pattern_header);
|
||||
}
|
||||
if (mDetailsText != null) {
|
||||
mDetailsTextView.setText(mDetailsText);
|
||||
} else {
|
||||
mDetailsTextView.setText(getDefaultDetails());
|
||||
}
|
||||
mErrorTextView.setText("");
|
||||
if (isProfileChallenge()) {
|
||||
updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
|
||||
mEffectiveUserId));
|
||||
}
|
||||
|
||||
mLockPatternView.setEnabled(true);
|
||||
mLockPatternView.enableInput();
|
||||
mLockPatternView.clearPattern();
|
||||
break;
|
||||
case NeedToUnlockWrong:
|
||||
mErrorTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
|
||||
|
||||
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
|
||||
mLockPatternView.setEnabled(true);
|
||||
mLockPatternView.enableInput();
|
||||
break;
|
||||
case LockedOut:
|
||||
mLockPatternView.clearPattern();
|
||||
// enabled = false means: disable input, and have the
|
||||
// appearance of being disabled.
|
||||
mLockPatternView.setEnabled(false); // appearance of being disabled
|
||||
break;
|
||||
}
|
||||
|
||||
// Always announce the header for accessibility. This is a no-op
|
||||
// when accessibility is disabled.
|
||||
mHeaderTextView.announceForAccessibility(mHeaderTextView.getText());
|
||||
}
|
||||
|
||||
private Runnable mClearPatternRunnable = new Runnable() {
|
||||
public void run() {
|
||||
mLockPatternView.clearPattern();
|
||||
}
|
||||
};
|
||||
|
||||
// clear the wrong pattern unless they have started a new one
|
||||
// already
|
||||
private void postClearPatternRunnable() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void authenticationSucceeded() {
|
||||
mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
|
||||
}
|
||||
|
||||
private void startDisappearAnimation(final Intent intent) {
|
||||
if (mDisappearing) {
|
||||
return;
|
||||
}
|
||||
mDisappearing = true;
|
||||
|
||||
final ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
|
||||
// Bail if there is no active activity.
|
||||
if (activity == null || activity.isFinishing()) {
|
||||
return;
|
||||
}
|
||||
if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
|
||||
mLockPatternView.clearPattern();
|
||||
mDisappearAnimationUtils.startAnimation2d(getActiveViews(),
|
||||
() -> {
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
activity.overridePendingTransition(
|
||||
R.anim.confirm_credential_close_enter,
|
||||
R.anim.confirm_credential_close_exit);
|
||||
}, this);
|
||||
} else {
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFingerprintIconVisibilityChanged(boolean visible) {
|
||||
if (mLeftSpacerLandscape != null && mRightSpacerLandscape != null) {
|
||||
|
||||
// In landscape, adjust spacing depending on fingerprint icon visibility.
|
||||
mLeftSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
|
||||
mRightSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The pattern listener that responds according to a user confirming
|
||||
* an existing lock pattern.
|
||||
*/
|
||||
private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener
|
||||
= new LockPatternView.OnPatternListener() {
|
||||
|
||||
public void onPatternStart() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
}
|
||||
|
||||
public void onPatternCleared() {
|
||||
mLockPatternView.removeCallbacks(mClearPatternRunnable);
|
||||
}
|
||||
|
||||
public void onPatternCellAdded(List<Cell> pattern) {
|
||||
|
||||
}
|
||||
|
||||
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
|
||||
if (mPendingLockCheck != null || mDisappearing) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLockPatternView.setEnabled(false);
|
||||
|
||||
final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
|
||||
Intent intent = new Intent();
|
||||
if (verifyChallenge) {
|
||||
if (isInternalActivity()) {
|
||||
startVerifyPattern(pattern, intent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startCheckPattern(pattern, intent);
|
||||
return;
|
||||
}
|
||||
|
||||
mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
|
||||
}
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return getActivity() instanceof ConfirmLockPattern.InternalActivity;
|
||||
}
|
||||
|
||||
private void startVerifyPattern(final List<LockPatternView.Cell> pattern,
|
||||
final Intent intent) {
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
final int localUserId = mUserId;
|
||||
long challenge = getActivity().getIntent().getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
|
||||
final LockPatternChecker.OnVerifyCallback onVerifyCallback =
|
||||
new LockPatternChecker.OnVerifyCallback() {
|
||||
@Override
|
||||
public void onVerified(byte[] token, int timeoutMs) {
|
||||
mPendingLockCheck = null;
|
||||
boolean matched = false;
|
||||
if (token != null) {
|
||||
matched = true;
|
||||
if (mReturnCredentials) {
|
||||
intent.putExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
||||
token);
|
||||
}
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localEffectiveUserId);
|
||||
}
|
||||
};
|
||||
mPendingLockCheck = (localEffectiveUserId == localUserId)
|
||||
? LockPatternChecker.verifyPattern(
|
||||
mLockPatternUtils, pattern, challenge, localUserId,
|
||||
onVerifyCallback)
|
||||
: LockPatternChecker.verifyTiedProfileChallenge(
|
||||
mLockPatternUtils, LockPatternUtils.patternToString(pattern),
|
||||
true, challenge, localUserId, onVerifyCallback);
|
||||
}
|
||||
|
||||
private void startCheckPattern(final List<LockPatternView.Cell> pattern,
|
||||
final Intent intent) {
|
||||
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
|
||||
// Pattern size is less than the minimum, do not count it as an fail attempt.
|
||||
onPatternChecked(false, intent, 0, mEffectiveUserId, false /* newResult */);
|
||||
return;
|
||||
}
|
||||
|
||||
final int localEffectiveUserId = mEffectiveUserId;
|
||||
mPendingLockCheck = LockPatternChecker.checkPattern(
|
||||
mLockPatternUtils,
|
||||
pattern,
|
||||
localEffectiveUserId,
|
||||
new LockPatternChecker.OnCheckCallback() {
|
||||
@Override
|
||||
public void onChecked(boolean matched, int timeoutMs) {
|
||||
mPendingLockCheck = null;
|
||||
if (matched && isInternalActivity() && mReturnCredentials) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
|
||||
StorageManager.CRYPT_TYPE_PATTERN);
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
|
||||
LockPatternUtils.patternToString(pattern));
|
||||
}
|
||||
mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
|
||||
localEffectiveUserId);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private void onPatternChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
mLockPatternView.setEnabled(true);
|
||||
if (matched) {
|
||||
if (newResult) {
|
||||
reportSuccessfullAttempt();
|
||||
}
|
||||
startDisappearAnimation(intent);
|
||||
checkForPendingIntent();
|
||||
} else {
|
||||
if (timeoutMs > 0) {
|
||||
refreshLockScreen();
|
||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
||||
effectiveUserId, timeoutMs);
|
||||
handleAttemptLockout(deadline);
|
||||
} else {
|
||||
updateStage(Stage.NeedToUnlockWrong);
|
||||
postClearPatternRunnable();
|
||||
}
|
||||
if (newResult) {
|
||||
reportFailedAttempt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult) {
|
||||
onPatternChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLastTryErrorMessage() {
|
||||
return R.string.lock_profile_wipe_warning_content_pattern;
|
||||
}
|
||||
|
||||
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
|
||||
updateStage(Stage.LockedOut);
|
||||
long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||
mCountdownTimer = new CountDownTimer(
|
||||
elapsedRealtimeDeadline - elapsedRealtime,
|
||||
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
|
||||
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
final int secondsCountdown = (int) (millisUntilFinished / 1000);
|
||||
mErrorTextView.setText(getString(
|
||||
R.string.lockpattern_too_many_failed_confirmation_attempts,
|
||||
secondsCountdown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
updateStage(Stage.NeedToUnlock);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAnimation(Object obj, long delay,
|
||||
long duration, float translationY, final boolean appearing,
|
||||
Interpolator interpolator,
|
||||
final Runnable finishListener) {
|
||||
if (obj instanceof LockPatternView.CellState) {
|
||||
final LockPatternView.CellState animatedCell = (LockPatternView.CellState) obj;
|
||||
mLockPatternView.startCellStateAnimation(animatedCell,
|
||||
1f, appearing ? 1f : 0f, /* alpha */
|
||||
appearing ? translationY : 0f, /* startTranslation */
|
||||
appearing ? 0f : translationY, /* endTranslation */
|
||||
appearing ? 0f : 1f, 1f /* scale */,
|
||||
delay, duration, interpolator, finishListener);
|
||||
} else {
|
||||
mAppearAnimationUtils.createAnimation((View) obj, delay, duration, translationY,
|
||||
appearing, interpolator, finishListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* An invisible retained fragment to track lock check result.
|
||||
*/
|
||||
public class CredentialCheckResultTracker extends Fragment {
|
||||
|
||||
private Listener mListener;
|
||||
private boolean mHasResult = false;
|
||||
|
||||
private boolean mResultMatched;
|
||||
private Intent mResultData;
|
||||
private int mResultTimeoutMs;
|
||||
private int mResultEffectiveUserId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
if (mListener == listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
if (mListener != null && mHasResult) {
|
||||
mListener.onCredentialChecked(mResultMatched, mResultData, mResultTimeoutMs,
|
||||
mResultEffectiveUserId, false /* newResult */);
|
||||
}
|
||||
}
|
||||
|
||||
public void setResult(boolean matched, Intent intent, int timeoutMs, int effectiveUserId) {
|
||||
mResultMatched = matched;
|
||||
mResultData = intent;
|
||||
mResultTimeoutMs = timeoutMs;
|
||||
mResultEffectiveUserId = effectiveUserId;
|
||||
|
||||
mHasResult = true;
|
||||
if (mListener != null) {
|
||||
mListener.onCredentialChecked(mResultMatched, mResultData, mResultTimeoutMs,
|
||||
mResultEffectiveUserId, true /* newResult */);
|
||||
mHasResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearResult() {
|
||||
mHasResult = false;
|
||||
mResultMatched = false;
|
||||
mResultData = null;
|
||||
mResultTimeoutMs = 0;
|
||||
mResultEffectiveUserId = 0;
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
|
||||
int effectiveUserId, boolean newResult);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Helper for handling managed passwords in security settings UI.
|
||||
* It provides resources that should be shown in settings UI when lock password quality is set to
|
||||
* {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_MANAGED} and hooks for implementing
|
||||
* an option for setting the password quality to
|
||||
* {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_MANAGED}.
|
||||
*/
|
||||
public class ManagedLockPasswordProvider {
|
||||
/** Factory method to make it easier to inject extended ManagedLockPasswordProviders. */
|
||||
public static ManagedLockPasswordProvider get(Context context, int userId) {
|
||||
return new ManagedLockPasswordProvider();
|
||||
}
|
||||
|
||||
protected ManagedLockPasswordProvider() {}
|
||||
|
||||
/**
|
||||
* Whether choosing/setting a managed lock password is supported for the user.
|
||||
* Please update {@link #getPickerOptionTitle(boolean)} if overridden to return true.
|
||||
*/
|
||||
boolean isSettingManagedPasswordSupported() { return false; }
|
||||
|
||||
/**
|
||||
* Whether the user should be able to choose managed lock password.
|
||||
*/
|
||||
boolean isManagedPasswordChoosable() { return false; }
|
||||
|
||||
/**
|
||||
* Returns title for managed password preference in security (lock) setting picker.
|
||||
* Should be overridden if {@link #isManagedPasswordSupported()} returns true.
|
||||
* @param forFingerprint Whether fingerprint unlock is enabled.
|
||||
*/
|
||||
String getPickerOptionTitle(boolean forFingerprint) { return ""; }
|
||||
|
||||
/**
|
||||
* Gets resource id of the lock screen preference that should be displayed in security settings
|
||||
* if the current password quality is set to
|
||||
* {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_MANAGED}.
|
||||
* @param forProfile Whether the settings are shown for a user profile rather than a user.
|
||||
*/
|
||||
public int getResIdForLockUnlockScreen(boolean forProfile) {
|
||||
return forProfile ? R.xml.security_settings_password_profile
|
||||
: R.xml.security_settings_password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets resource id of the subscreen that should be shown after clicking gear icon for lock
|
||||
* screen preference in security settings if the current password quality is set to
|
||||
* {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_MANAGED}.
|
||||
*/
|
||||
public int getResIdForLockUnlockSubScreen() {
|
||||
return R.xml.security_settings_password_sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates intent that should be launched when user chooses managed password in the lock
|
||||
* settings picker.
|
||||
* @param requirePasswordToDecrypt Whether a password is needed to decrypt the user.
|
||||
* @param password Current lock password.
|
||||
* @return Intent that should update lock password to a managed password.
|
||||
*/
|
||||
Intent createIntent(boolean requirePasswordToDecrypt, String password) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,10 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import static com.android.settings.password.PasswordRequirementAdapter
|
||||
.PasswordRequirementViewHolder;
|
||||
import com.android.settings.password.PasswordRequirementAdapter.PasswordRequirementViewHolder;
|
||||
|
||||
/**
|
||||
* Used in {@link com.android.settings.ChooseLockPassword} to show password requirements.
|
||||
* Used in {@link ChooseLockPassword} to show password requirements.
|
||||
*/
|
||||
public class PasswordRequirementAdapter extends
|
||||
RecyclerView.Adapter<PasswordRequirementViewHolder> {
|
||||
|
||||
125
src/com/android/settings/password/SaveChosenLockWorkerBase.java
Normal file
125
src/com/android/settings/password/SaveChosenLockWorkerBase.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
/**
|
||||
* An invisible retained worker fragment to track the AsyncWork that saves (and optionally
|
||||
* verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
|
||||
*/
|
||||
abstract class SaveChosenLockWorkerBase extends Fragment {
|
||||
|
||||
private Listener mListener;
|
||||
private boolean mFinished;
|
||||
private Intent mResultData;
|
||||
|
||||
protected LockPatternUtils mUtils;
|
||||
protected boolean mHasChallenge;
|
||||
protected long mChallenge;
|
||||
protected boolean mWasSecureBefore;
|
||||
protected int mUserId;
|
||||
|
||||
private boolean mBlocking;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
if (mListener == listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
if (mFinished && mListener != null) {
|
||||
mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepare(LockPatternUtils utils, boolean credentialRequired,
|
||||
boolean hasChallenge, long challenge, int userId) {
|
||||
mUtils = utils;
|
||||
mUserId = userId;
|
||||
|
||||
mHasChallenge = hasChallenge;
|
||||
mChallenge = challenge;
|
||||
// This will be a no-op for non managed profiles.
|
||||
mWasSecureBefore = mUtils.isSecure(mUserId);
|
||||
|
||||
Context context = getContext();
|
||||
// If context is null, we're being invoked to change the setCredentialRequiredToDecrypt,
|
||||
// and we made sure that this is the primary user already.
|
||||
if (context == null || UserManager.get(context).getUserInfo(mUserId).isPrimary()) {
|
||||
mUtils.setCredentialRequiredToDecrypt(credentialRequired);
|
||||
}
|
||||
|
||||
mFinished = false;
|
||||
mResultData = null;
|
||||
}
|
||||
|
||||
protected void start() {
|
||||
if (mBlocking) {
|
||||
finish(saveAndVerifyInBackground());
|
||||
} else {
|
||||
new Task().execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the save and verify work in background.
|
||||
* @return Intent with challenge token or null.
|
||||
*/
|
||||
protected abstract Intent saveAndVerifyInBackground();
|
||||
|
||||
protected void finish(Intent resultData) {
|
||||
mFinished = true;
|
||||
mResultData = resultData;
|
||||
if (mListener != null) {
|
||||
mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlocking(boolean blocking) {
|
||||
mBlocking = blocking;
|
||||
}
|
||||
|
||||
private class Task extends AsyncTask<Void, Void, Intent> {
|
||||
@Override
|
||||
protected Intent doInBackground(Void... params){
|
||||
return saveAndVerifyInBackground();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Intent resultData) {
|
||||
finish(resultData);
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.ChooseLockGeneric;
|
||||
import com.android.settings.SetupChooseLockGeneric;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settings.password;
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
@@ -34,8 +35,6 @@ import android.os.UserManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.ChooseLockGeneric;
|
||||
import com.android.settings.ChooseLockSettingsHelper;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
/**
|
||||
|
||||
209
src/com/android/settings/password/SetupChooseLockGeneric.java
Normal file
209
src/com/android/settings/password/SetupChooseLockGeneric.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.support.v14.preference.PreferenceFragment;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupEncryptionInterstitial;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.fingerprint.SetupFingerprintEnrollFindSensor;
|
||||
import com.android.settings.fingerprint.SetupSkipDialog;
|
||||
import com.android.settings.utils.SettingsDividerItemDecoration;
|
||||
import com.android.setupwizardlib.GlifPreferenceLayout;
|
||||
|
||||
/**
|
||||
* Setup Wizard's version of ChooseLockGeneric screen. It inherits the logic and basic structure
|
||||
* from ChooseLockGeneric class, and should remain similar to that behaviorally. This class should
|
||||
* only overload base methods for minor theme and behavior differences specific to Setup Wizard.
|
||||
* Other changes should be done to ChooseLockGeneric class instead and let this class inherit
|
||||
* those changes.
|
||||
*/
|
||||
public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
|
||||
private static final String KEY_UNLOCK_SET_DO_LATER = "unlock_set_do_later";
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return SetupChooseLockGenericFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
/* package */ Class<? extends PreferenceFragment> getFragmentClass() {
|
||||
return SetupChooseLockGenericFragment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
resid = SetupWizardUtils.getTheme(getIntent());
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
public static class SetupChooseLockGenericFragment extends ChooseLockGenericFragment {
|
||||
|
||||
public static final String EXTRA_PASSWORD_QUALITY = ":settings:password_quality";
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
|
||||
layout.setDividerItemDecoration(new SettingsDividerItemDecoration(getContext()));
|
||||
layout.setDividerInset(getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.suw_items_glif_text_divider_inset));
|
||||
|
||||
layout.setIcon(getContext().getDrawable(R.drawable.ic_lock));
|
||||
|
||||
int titleResource = mForFingerprint ?
|
||||
R.string.lock_settings_picker_title : R.string.setup_lock_settings_picker_title;
|
||||
if (getActivity() != null) {
|
||||
getActivity().setTitle(titleResource);
|
||||
}
|
||||
|
||||
layout.setHeaderText(titleResource);
|
||||
// Use the dividers in SetupWizardRecyclerLayout. Suppress the dividers in
|
||||
// PreferenceFragment.
|
||||
setDivider(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addHeaderView() {
|
||||
if (mForFingerprint) {
|
||||
setHeaderView(R.layout.setup_choose_lock_generic_fingerprint_header);
|
||||
} else {
|
||||
setHeaderView(R.layout.setup_choose_lock_generic_header);
|
||||
}
|
||||
}
|
||||
|
||||
@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 the started activity was cancelled (e.g. the user presses back), then this
|
||||
// activity will be resumed to foreground.
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
|
||||
Bundle savedInstanceState) {
|
||||
GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
|
||||
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
|
||||
}
|
||||
|
||||
/***
|
||||
* Disables preferences that are less secure than required quality and shows only secure
|
||||
* screen lock options here.
|
||||
*
|
||||
* @param quality the requested quality.
|
||||
*/
|
||||
@Override
|
||||
protected void disableUnusablePreferences(final int quality, boolean hideDisabled) {
|
||||
// At this part of the flow, the user has already indicated they want to add a pin,
|
||||
// pattern or password, so don't show "None" or "Slide". We disable them here and set
|
||||
// the HIDE_DISABLED flag to true to hide them. This only happens for setup wizard.
|
||||
// We do the following max check here since the device may already have a Device Admin
|
||||
// installed with a policy we need to honor.
|
||||
final int newQuality = Math.max(quality,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
|
||||
super.disableUnusablePreferencesImpl(newQuality, true /* hideDisabled */);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPreferences() {
|
||||
if (mForFingerprint) {
|
||||
super.addPreferences();
|
||||
} else {
|
||||
addPreferencesFromResource(R.xml.setup_security_settings_picker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
if (KEY_UNLOCK_SET_DO_LATER.equals(key)) {
|
||||
// show warning.
|
||||
SetupSkipDialog dialog = SetupSkipDialog.newInstance(getActivity().getIntent()
|
||||
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
|
||||
dialog.show(getFragmentManager());
|
||||
return true;
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getLockPasswordIntent(int quality, int minLength, int maxLength) {
|
||||
final Intent intent = SetupChooseLockPassword.modifyIntentForSetup(
|
||||
getContext(), super.getLockPasswordIntent(quality, minLength, maxLength));
|
||||
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getLockPatternIntent() {
|
||||
final Intent intent = SetupChooseLockPattern.modifyIntentForSetup(
|
||||
getContext(), super.getLockPatternIntent());
|
||||
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getEncryptionInterstitialIntent(Context context, int quality,
|
||||
boolean required, Intent unlockMethodIntent) {
|
||||
Intent intent = SetupEncryptionInterstitial.createStartIntent(context, quality,
|
||||
required, unlockMethodIntent);
|
||||
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getFindSensorIntent(Context context) {
|
||||
final Intent intent = new Intent(context, SetupFingerprintEnrollFindSensor.class);
|
||||
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupRedactionInterstitial;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
|
||||
/**
|
||||
* Setup Wizard's version of ChooseLockPassword screen. It inherits the logic and basic structure
|
||||
* from ChooseLockPassword class, and should remain similar to that behaviorally. This class should
|
||||
* only overload base methods for minor theme and behavior differences specific to Setup Wizard.
|
||||
* Other changes should be done to ChooseLockPassword class instead and let this class inherit
|
||||
* those changes.
|
||||
*/
|
||||
public class SetupChooseLockPassword extends ChooseLockPassword {
|
||||
|
||||
public static Intent modifyIntentForSetup(
|
||||
Context context,
|
||||
Intent chooseLockPasswordIntent) {
|
||||
chooseLockPasswordIntent.setClass(context, SetupChooseLockPassword.class);
|
||||
chooseLockPasswordIntent.putExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false);
|
||||
return chooseLockPasswordIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return SetupChooseLockPasswordFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
/* package */ Class<? extends Fragment> getFragmentClass() {
|
||||
return SetupChooseLockPasswordFragment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
resid = SetupWizardUtils.getTheme(getIntent());
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
|
||||
public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment {
|
||||
|
||||
@Override
|
||||
protected Intent getRedactionInterstitialIntent(Context context) {
|
||||
// Setup wizard's redaction interstitial is deferred to optional step. Enable that
|
||||
// optional step if the lock screen was set up.
|
||||
SetupRedactionInterstitial.setEnabled(context, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.android.settings.SetupRedactionInterstitial;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
|
||||
/**
|
||||
* Setup Wizard's version of ChooseLockPattern screen. It inherits the logic and basic structure
|
||||
* from ChooseLockPattern class, and should remain similar to that behaviorally. This class should
|
||||
* only overload base methods for minor theme and behavior differences specific to Setup Wizard.
|
||||
* Other changes should be done to ChooseLockPattern class instead and let this class inherit
|
||||
* those changes.
|
||||
*/
|
||||
public class SetupChooseLockPattern extends ChooseLockPattern {
|
||||
|
||||
public static Intent modifyIntentForSetup(Context context, Intent chooseLockPatternIntent) {
|
||||
chooseLockPatternIntent.setClass(context, SetupChooseLockPattern.class);
|
||||
return chooseLockPatternIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return SetupChooseLockPatternFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
/* package */ Class<? extends Fragment> getFragmentClass() {
|
||||
return SetupChooseLockPatternFragment.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
resid = SetupWizardUtils.getTheme(getIntent());
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
}
|
||||
|
||||
public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment {
|
||||
|
||||
@Override
|
||||
protected Intent getRedactionInterstitialIntent(Context context) {
|
||||
// Setup wizard's redaction interstitial is deferred to optional step. Enable that
|
||||
// optional step if the lock screen was set up.
|
||||
SetupRedactionInterstitial.setEnabled(context, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user