Fix 3148496: Initial pass at fragmentizing lockscreen settings.

This converts most of the existing activities to fragments and wraps
them in PreferenceActivities so they can be launched as before
(e.g. by a DevicePolicyManager)

Upload after sync/rebase.

Change-Id: I4f351b75d9fca0498bcb04b4e11ff3b70765a4ba
This commit is contained in:
Jim Miller
2010-12-07 20:41:41 -08:00
parent 1c7e49ba67
commit 17e9e19330
17 changed files with 2120 additions and 1267 deletions

View File

@@ -18,6 +18,7 @@ package com.android.settings;
import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -28,179 +29,190 @@ import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
public class ChooseLockGeneric extends PreferenceActivity {
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 int CONFIRM_EXISTING_REQUEST = 100;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private DevicePolicyManager mDPM;
private boolean mPasswordConfirmed = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockGenericFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
public static class ChooseLockGenericFragment extends SettingsPreferenceFragment {
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 int CONFIRM_EXISTING_REQUEST = 100;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
if (savedInstanceState != null) {
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
}
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private DevicePolicyManager mDPM;
private boolean mPasswordConfirmed = false;
if (!mPasswordConfirmed) {
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
mPasswordConfirmed = true; // no password set, so no need to confirm
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
if (savedInstanceState != null) {
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
}
if (!mPasswordConfirmed) {
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this);
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
mPasswordConfirmed = true; // no password set, so no need to confirm
updatePreferencesOrFinish();
}
} else {
updatePreferencesOrFinish();
}
} else {
updatePreferencesOrFinish();
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
final String key = preference.getKey();
boolean handled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true);
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false);
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
} else {
handled = false;
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
final String key = preference.getKey();
boolean handled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true);
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, false);
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, false);
} else {
handled = false;
}
return handled;
}
return handled;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == RESULT_OK) {
mPasswordConfirmed = true;
updatePreferencesOrFinish();
} else {
setResult(RESULT_CANCELED);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
mPasswordConfirmed = true;
updatePreferencesOrFinish();
} else {
getActivity().setResult(Activity.RESULT_CANCELED);
finish();
}
}
@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);
}
private void updatePreferencesOrFinish() {
int quality = getActivity().getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality == -1) {
// If caller didn't specify password quality, show the UI and allow the user to choose.
quality = mChooseLockSettingsHelper.utils().getKeyguardStoredPasswordQuality();
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
}
addPreferencesFromResource(R.xml.security_settings_picker);
disableUnusablePreferences(mDPM.getPasswordQuality(null));
} else {
updateUnlockMethodAndFinish(quality, false);
}
}
/***
* Disables preferences that are less secure than required quality.
*
* @param quality the requested quality.
*/
private void disableUnusablePreferences(final int quality) {
final Preference picker = getPreferenceScreen().findPreference("security_picker_category");
final PreferenceCategory cat = (PreferenceCategory) picker;
final int preferenceCount = cat.getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
Preference pref = cat.getPreference(i);
if (pref instanceof PreferenceScreen) {
final String key = ((PreferenceScreen) pref).getKey();
boolean enabled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
}
if (!enabled) {
pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
pref.setEnabled(false);
}
}
}
}
/**
* 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 first.
if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming first");
}
// Compare minimum allowed password quality and launch appropriate security setting method
int minQuality = mDPM.getPasswordQuality(null);
if (quality < minQuality) {
quality = minQuality;
}
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
minLength = MIN_PASSWORD_LENGTH;
}
final int maxLength = mDPM.getPasswordMaximumLength(quality);
Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class);
intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
boolean showTutorial = !mChooseLockSettingsHelper.utils().isPatternEverChosen();
Intent intent = new Intent();
intent.setClass(getActivity(), showTutorial
? ChooseLockPatternTutorial.class
: ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("key_lock_method", "pattern");
intent.putExtra(CONFIRM_CREDENTIALS, false);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
mChooseLockSettingsHelper.utils().clearLock();
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
getActivity().setResult(Activity.RESULT_OK);
}
finish();
}
}
@Override
protected 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);
}
private void updatePreferencesOrFinish() {
int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality == -1) {
// If caller didn't specify password quality, show the UI and allow the user to choose.
quality = mChooseLockSettingsHelper.utils().getKeyguardStoredPasswordQuality();
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
}
addPreferencesFromResource(R.xml.security_settings_picker);
disableUnusablePreferences(mDPM.getPasswordQuality(null));
} else {
updateUnlockMethodAndFinish(quality, false);
}
}
/***
* Disables preferences that are less secure than required quality.
*
* @param quality the requested quality.
*/
private void disableUnusablePreferences(final int quality) {
final Preference picker = getPreferenceScreen().findPreference("security_picker_category");
final PreferenceCategory cat = (PreferenceCategory) picker;
final int preferenceCount = cat.getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
Preference pref = cat.getPreference(i);
if (pref instanceof PreferenceScreen) {
final String key = ((PreferenceScreen) pref).getKey();
boolean enabled = true;
if (KEY_UNLOCK_SET_OFF.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
} else if (KEY_UNLOCK_SET_PIN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
} else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
}
if (!enabled) {
pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
pref.setEnabled(false);
}
}
}
}
/**
* 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 first.
if (!mPasswordConfirmed) {
throw new IllegalStateException("Tried to update password without confirming first");
}
// Compare minimum allowed password quality and launch appropriate security setting method
int minQuality = mDPM.getPasswordQuality(null);
if (quality < minQuality) {
quality = minQuality;
}
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
minLength = MIN_PASSWORD_LENGTH;
}
final int maxLength = mDPM.getPasswordMaximumLength(quality);
Intent intent = new Intent().setClass(this, ChooseLockPassword.class);
intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
boolean showTutorial = !mChooseLockSettingsHelper.utils().isPatternEverChosen();
Intent intent = new Intent();
intent.setClass(this, showTutorial
? ChooseLockPatternTutorial.class
: ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("key_lock_method", "pattern");
intent.putExtra(CONFIRM_CREDENTIALS, false);
startActivity(intent);
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
mChooseLockSettingsHelper.utils().clearLock();
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
setResult(RESULT_OK);
}
finish();
}
}

View File

@@ -21,19 +21,22 @@ import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceActivity;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
@@ -41,30 +44,7 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class ChooseLockPassword extends Activity implements OnClickListener, OnEditorActionListener,
TextWatcher {
private static final String KEY_FIRST_PIN = "first_pin";
private static final String KEY_UI_STAGE = "ui_stage";
private TextView mPasswordEntry;
private int mPasswordMinLength = 4;
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 LockPatternUtils mLockPatternUtils;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private com.android.settings.ChooseLockPassword.Stage mUiStage = Stage.Introduction;
private TextView mHeaderText;
private String mFirstPin;
private KeyboardView mKeyboardView;
private PasswordEntryKeyboardHelper mKeyboardHelper;
private boolean mIsAlphaMode;
private Button mCancelButton;
private Button mNextButton;
public class ChooseLockPassword extends PreferenceActivity {
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";
@@ -73,345 +53,393 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE
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 Handler mHandler = new Handler();
private static final int CONFIRM_EXISTING_REQUEST = 58;
static final int RESULT_FINISHED = RESULT_FIRST_USER;
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
/**
* Keep track internally of where the user is in choosing a pattern.
*/
protected enum Stage {
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPasswordFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
Introduction(R.string.lockpassword_choose_your_password_header,
R.string.lockpassword_choose_your_pin_header,
R.string.lockpassword_continue_label),
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO: Fix on phones
// Disable IME on our window since we provide our own keyboard
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
//WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
super.onCreate(savedInstanceState);
}
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);
public static class ChooseLockPasswordFragment extends Fragment
implements OnClickListener, OnEditorActionListener, TextWatcher {
private static final String KEY_FIRST_PIN = "first_pin";
private static final String KEY_UI_STAGE = "ui_stage";
private TextView mPasswordEntry;
private int mPasswordMinLength = 4;
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 LockPatternUtils mLockPatternUtils;
private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private Stage mUiStage = Stage.Introduction;
private TextView mHeaderText;
private String mFirstPin;
private KeyboardView mKeyboardView;
private PasswordEntryKeyboardHelper mKeyboardHelper;
private boolean mIsAlphaMode;
private Button mCancelButton;
private Button mNextButton;
private static Handler mHandler = new Handler();
private static final int CONFIRM_EXISTING_REQUEST = 58;
static final int RESULT_FINISHED = RESULT_FIRST_USER;
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
/**
* @param headerMessage The message displayed at the top.
* Keep track internally of where the user is in choosing a pattern.
*/
Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
this.alphaHint = hintInAlpha;
this.numericHint = hintInNumeric;
this.buttonText = nextButtonText;
}
protected enum Stage {
public final int alphaHint;
public final int numericHint;
public final int buttonText;
}
Introduction(R.string.lockpassword_choose_your_password_header,
R.string.lockpassword_choose_your_pin_header,
R.string.lockpassword_continue_label),
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(this);
mRequestedQuality = Math.max(getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
mPasswordMinLength = Math.max(
getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
.getRequestedMinimumPasswordLength());
mPasswordMaxLength = getIntent().getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
mPasswordMinLetters = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LETTERS_KEY,
mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
mPasswordMinUpperCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
mPasswordMinLowerCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
mPasswordMinNumeric = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
mPasswordMinSymbols = Math.max(getIntent().getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
mPasswordMinNonLetter = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true);
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);
initViews();
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
/**
* @param headerMessage The message displayed at the top.
*/
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;
}
}
private void initViews() {
setContentView(R.layout.choose_lock_password);
// Disable IME on our window since we provide our own keyboard
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
// required constructor for fragments
public ChooseLockPasswordFragment() {
mCancelButton = (Button) findViewById(R.id.cancel_button);
mCancelButton.setOnClickListener(this);
mNextButton = (Button) findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
}
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
mPasswordEntry = (TextView) findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mPasswordEntry.addTextChangedListener(this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getActivity());
Intent intent = getActivity().getIntent();
mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
mPasswordMinLength = Math.max(
intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
.getRequestedMinimumPasswordLength());
mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
}
mHeaderText = (TextView) findViewById(R.id.headerText);
mKeyboardView.requestFocus();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@Override
protected void onResume() {
super.onResume();
updateStage(mUiStage);
mKeyboardView.requestFocus();
}
View view = inflater.inflate(R.layout.choose_lock_password, null);
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_UI_STAGE, mUiStage.name());
outState.putString(KEY_FIRST_PIN, mFirstPin);
}
mCancelButton = (Button) view.findViewById(R.id.cancel_button);
mCancelButton.setOnClickListener(this);
mNextButton = (Button) view.findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String state = savedInstanceState.getString(KEY_UI_STAGE);
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
if (state != null) {
mUiStage = Stage.valueOf(state);
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mPasswordEntry.addTextChangedListener(this);
mKeyboardHelper = new PasswordEntryKeyboardHelper(getActivity(),
mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
mKeyboardView.requestFocus();
Intent intent = getActivity().getIntent();
final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
}
} else {
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
final String state = savedInstanceState.getString(KEY_UI_STAGE);
if (state != null) {
mUiStage = Stage.valueOf(state);
updateStage(mUiStage);
}
}
return view;
}
@Override
public void onResume() {
super.onResume();
updateStage(mUiStage);
mKeyboardView.requestFocus();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CONFIRM_EXISTING_REQUEST:
if (resultCode != Activity.RESULT_OK) {
setResult(RESULT_FINISHED);
finish();
}
break;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_UI_STAGE, mUiStage.name());
outState.putString(KEY_FIRST_PIN, mFirstPin);
}
}
protected void updateStage(Stage stage) {
mUiStage = stage;
updateUi();
}
/**
* Validates PIN and returns a message to display if PIN fails test.
* @param password the raw password the user typed in
* @return error message to show to user or null if password is OK
*/
private String validatePassword(String password) {
if (password.length() < mPasswordMinLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength);
}
if (password.length() > mPasswordMaxLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_long
: R.string.lockpassword_pin_too_long, mPasswordMaxLength);
}
int letters = 0;
int numbers = 0;
int lowercase = 0;
int symbols = 0;
int uppercase = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
// allow non white space Latin-1 characters only
if (c <= 32 || c > 127) {
return getString(R.string.lockpassword_illegal_character);
}
if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else {
symbols++;
nonletter++;
@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();
}
break;
}
}
if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
&& (letters > 0 || symbols > 0)) {
// This shouldn't be possible unless user finds some way to bring up
// soft keyboard
return getString(R.string.lockpassword_pin_contains_non_digits);
} else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
if (letters < mPasswordMinLetters) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
mPasswordMinLetters);
} else if (numbers < mPasswordMinNumeric) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
mPasswordMinNumeric);
} else if (lowercase < mPasswordMinLowerCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
mPasswordMinLowerCase);
} else if (uppercase < mPasswordMinUpperCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
mPasswordMinUpperCase);
} else if (symbols < mPasswordMinSymbols) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
mPasswordMinSymbols);
} else if (nonletter < mPasswordMinNonLetter) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
mPasswordMinNonLetter);
}
} else {
final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
== mRequestedQuality;
final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
== mRequestedQuality;
if ((alphabetic || alphanumeric) && letters == 0) {
return getString(R.string.lockpassword_password_requires_alpha);
}
if (alphanumeric && numbers == 0) {
return getString(R.string.lockpassword_password_requires_digit);
}
}
if(mLockPatternUtils.checkPasswordHistory(password)) {
return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
: R.string.lockpassword_pin_recently_used);
}
return null;
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (TextUtils.isEmpty(pin)) {
return;
protected void updateStage(Stage stage) {
mUiStage = stage;
updateUi();
}
String errorMsg = null;
if (mUiStage == Stage.Introduction) {
errorMsg = validatePassword(pin);
if (errorMsg == null) {
mFirstPin = pin;
updateStage(Stage.NeedToConfirm);
mPasswordEntry.setText("");
}
} else if (mUiStage == Stage.NeedToConfirm) {
if (mFirstPin.equals(pin)) {
mLockPatternUtils.clearLock();
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality);
finish();
} else {
updateStage(Stage.ConfirmWrong);
CharSequence tmp = mPasswordEntry.getText();
if (tmp != null) {
Selection.setSelection((Spannable) tmp, 0, tmp.length());
}
}
}
if (errorMsg != null) {
showError(errorMsg, mUiStage);
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
finish();
break;
}
}
private void showError(String msg, final Stage next) {
mHeaderText.setText(msg);
mHandler.postDelayed(new Runnable() {
public void run() {
updateStage(next);
}
}, ERROR_MESSAGE_TIMEOUT);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) {
handleNext();
return true;
}
return false;
}
/**
* Update the hint based on current Stage and length of password entry
*/
private void updateUi() {
String password = mPasswordEntry.getText().toString();
final int length = password.length();
if (mUiStage == Stage.Introduction && length > 0) {
if (length < mPasswordMinLength) {
String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
/**
* Validates PIN and returns a message to display if PIN fails test.
* @param password the raw password the user typed in
* @return error message to show to user or null if password is OK
*/
private String validatePassword(String password) {
if (password.length() < mPasswordMinLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength);
mHeaderText.setText(msg);
mNextButton.setEnabled(false);
}
if (password.length() > mPasswordMaxLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_long
: R.string.lockpassword_pin_too_long, mPasswordMaxLength);
}
int letters = 0;
int numbers = 0;
int lowercase = 0;
int symbols = 0;
int uppercase = 0;
int nonletter = 0;
for (int i = 0; i < password.length(); i++) {
char c = password.charAt(i);
// allow non white space Latin-1 characters only
if (c <= 32 || c > 127) {
return getString(R.string.lockpassword_illegal_character);
}
if (c >= '0' && c <= '9') {
numbers++;
nonletter++;
} else if (c >= 'A' && c <= 'Z') {
letters++;
uppercase++;
} else if (c >= 'a' && c <= 'z') {
letters++;
lowercase++;
} else {
symbols++;
nonletter++;
}
}
if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
&& (letters > 0 || symbols > 0)) {
// This shouldn't be possible unless user finds some way to bring up
// soft keyboard
return getString(R.string.lockpassword_pin_contains_non_digits);
} else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
if (letters < mPasswordMinLetters) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
mPasswordMinLetters);
} else if (numbers < mPasswordMinNumeric) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
mPasswordMinNumeric);
} else if (lowercase < mPasswordMinLowerCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
mPasswordMinLowerCase);
} else if (uppercase < mPasswordMinUpperCase) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
mPasswordMinUpperCase);
} else if (symbols < mPasswordMinSymbols) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
mPasswordMinSymbols);
} else if (nonletter < mPasswordMinNonLetter) {
return String.format(getResources().getQuantityString(
R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
mPasswordMinNonLetter);
}
} else {
String error = validatePassword(password);
if (error != null) {
mHeaderText.setText(error);
final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
== mRequestedQuality;
final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
== mRequestedQuality;
if ((alphabetic || alphanumeric) && letters == 0) {
return getString(R.string.lockpassword_password_requires_alpha);
}
if (alphanumeric && numbers == 0) {
return getString(R.string.lockpassword_password_requires_digit);
}
}
if(mLockPatternUtils.checkPasswordHistory(password)) {
return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
: R.string.lockpassword_pin_recently_used);
}
return null;
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (TextUtils.isEmpty(pin)) {
return;
}
String errorMsg = null;
if (mUiStage == Stage.Introduction) {
errorMsg = validatePassword(pin);
if (errorMsg == null) {
mFirstPin = pin;
updateStage(Stage.NeedToConfirm);
mPasswordEntry.setText("");
}
} else if (mUiStage == Stage.NeedToConfirm) {
if (mFirstPin.equals(pin)) {
mLockPatternUtils.clearLock();
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality);
getActivity().finish();
} else {
updateStage(Stage.ConfirmWrong);
CharSequence tmp = mPasswordEntry.getText();
if (tmp != null) {
Selection.setSelection((Spannable) tmp, 0, tmp.length());
}
}
}
if (errorMsg != null) {
showError(errorMsg, mUiStage);
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
getActivity().finish();
break;
}
}
private void showError(String msg, final Stage next) {
mHeaderText.setText(msg);
mHandler.postDelayed(new Runnable() {
public void run() {
updateStage(next);
}
}, ERROR_MESSAGE_TIMEOUT);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) {
handleNext();
return true;
}
return false;
}
/**
* Update the hint based on current Stage and length of password entry
*/
private void updateUi() {
String password = mPasswordEntry.getText().toString();
final int length = password.length();
if (mUiStage == Stage.Introduction && length > 0) {
if (length < mPasswordMinLength) {
String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
: R.string.lockpassword_pin_too_short, mPasswordMinLength);
mHeaderText.setText(msg);
mNextButton.setEnabled(false);
} else {
mHeaderText.setText(R.string.lockpassword_press_continue);
mNextButton.setEnabled(true);
String error = validatePassword(password);
if (error != null) {
mHeaderText.setText(error);
mNextButton.setEnabled(false);
} else {
mHeaderText.setText(R.string.lockpassword_press_continue);
mNextButton.setEnabled(true);
}
}
} else {
mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
mNextButton.setEnabled(length > 0);
}
} else {
mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
mNextButton.setEnabled(length > 0);
mNextButton.setText(mUiStage.buttonText);
}
mNextButton.setText(mUiStage.buttonText);
}
public void afterTextChanged(Editable s) {
// Changing the text while error displayed resets to NeedToConfirm state
if (mUiStage == Stage.ConfirmWrong) {
mUiStage = Stage.NeedToConfirm;
public void afterTextChanged(Editable s) {
// Changing the text while error displayed resets to NeedToConfirm state
if (mUiStage == Stage.ConfirmWrong) {
mUiStage = Stage.NeedToConfirm;
}
updateUi();
}
updateUi();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
}

View File

@@ -22,14 +22,18 @@ import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell;
import static com.android.internal.widget.LockPatternView.DisplayMode;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
@@ -44,7 +48,7 @@ import java.util.List;
* - asks for confirmation / restart
* - saves chosen password when confirmed
*/
public class ChooseLockPattern extends Activity implements View.OnClickListener{
public class ChooseLockPattern extends PreferenceActivity {
/**
* Used by the choose lock pattern wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
@@ -56,440 +60,461 @@ public class ChooseLockPattern extends Activity implements View.OnClickListener{
*/
static final int RESULT_FINISHED = RESULT_FIRST_USER;
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;
protected TextView mHeaderText;
protected LockPatternView mLockPatternView;
protected TextView mFooterText;
private TextView mFooterLeftButton;
private TextView mFooterRightButton;
protected List<LockPatternView.Cell> mChosenPattern = null;
/**
* 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 Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CONFIRM_EXISTING_REQUEST:
if (resultCode != Activity.RESULT_OK) {
setResult(RESULT_FINISHED);
finish();
}
updateStage(Stage.Introduction);
break;
}
}
/**
* 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);
}
};
/**
* 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,
R.string.lockpattern_recording_intro_footer, 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.CancelDisabled, 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 static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setupViews();
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true);
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,
null, null);
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);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
}
}
/**
* Keep all "find view" related stuff confined to this function since in
* case someone needs to subclass and customize.
*/
protected void setupViews() {
setContentView(R.layout.choose_lock_pattern);
mHeaderText = (TextView) findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
mLockPatternView.setTactileFeedbackEnabled(
mChooseLockSettingsHelper.utils().isTactileFeedbackEnabled());
mFooterText = (TextView) findViewById(R.id.footerText);
mFooterLeftButton = (TextView) findViewById(R.id.footerLeftButton);
mFooterRightButton = (TextView) findViewById(R.id.footerRightButton);
mFooterLeftButton.setOnClickListener(this);
mFooterRightButton.setOnClickListener(this);
}
public void onClick(View v) {
if (v == mFooterLeftButton) {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
mChosenPattern = null;
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
// They are canceling the entire wizard
setResult(RESULT_FINISHED);
finish();
} else {
throw new IllegalStateException("left footer button pressed, but stage of " +
mUiStage + " doesn't make sense");
}
} else if (v == mFooterRightButton) {
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);
}
saveChosenPatternAndFinish();
} 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);
}
}
}
@Override
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;
}
// *** TODO ***
// chooseLockPatternFragment.onKeyDown(keyCode, event);
return super.onKeyDown(keyCode, event);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
public static class ChooseLockPatternFragment extends Fragment
implements View.OnClickListener {
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
if (mChosenPattern != null) {
outState.putString(KEY_PATTERN_CHOICE, LockPatternUtils.patternToString(mChosenPattern));
}
}
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;
/**
* 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) {
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
mUiStage = stage;
private static final int ID_EMPTY_MESSAGE = -1;
// 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);
protected TextView mHeaderText;
protected LockPatternView mLockPatternView;
protected TextView mFooterText;
private TextView mFooterLeftButton;
private TextView mFooterRightButton;
protected List<LockPatternView.Cell> mChosenPattern = null;
/**
* 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();
}
updateStage(Stage.Introduction);
break;
}
}
if (stage.leftMode == LeftButtonMode.Gone) {
mFooterLeftButton.setVisibility(View.GONE);
} else {
mFooterLeftButton.setVisibility(View.VISIBLE);
mFooterLeftButton.setText(stage.leftMode.text);
mFooterLeftButton.setEnabled(stage.leftMode.enabled);
/**
* 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);
}
};
/**
* 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;
}
mFooterRightButton.setText(stage.rightMode.text);
mFooterRightButton.setEnabled(stage.rightMode.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);
// same for whether the patten is enabled
if (stage.patternEnabled) {
mLockPatternView.enableInput();
} else {
mLockPatternView.disableInput();
/**
* @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;
}
// the rest of the stuff varies enough that it is easier just to handle
// on a case by case basis.
mLockPatternView.setDisplayMode(DisplayMode.Correct);
/**
* Keep track internally of where the user is in choosing a pattern.
*/
protected enum Stage {
switch (mUiStage) {
case Introduction:
Introduction(
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
R.string.lockpattern_recording_intro_footer, 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.CancelDisabled, 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();
break;
case HelpScreen:
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
break;
case ChoiceTooShort:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case FirstChoiceValid:
break;
case NeedToConfirm:
mLockPatternView.clearPattern();
break;
case ConfirmWrong:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case ChoiceConfirmed:
break;
}
}
}
};
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
// 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 static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
private void saveChosenPatternAndFinish() {
LockPatternUtils utils = mChooseLockSettingsHelper.utils();
final boolean lockVirgin = !utils.isPatternEverChosen();
utils.saveLockPattern(mChosenPattern);
utils.setLockPatternEnabled(true);
if (lockVirgin) {
utils.setVisiblePatternEnabled(true);
utils.setTactileFeedbackEnabled(false);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
}
setResult(RESULT_FINISHED);
finish();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// setupViews()
View view = inflater.inflate(R.layout.choose_lock_pattern, null);
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);
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("confirm_credentials", true);
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, null, null);
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);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
}
return view;
}
public void onClick(View v) {
if (v == mFooterLeftButton) {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
mChosenPattern = null;
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
// They are canceling the entire wizard
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
} else {
throw new IllegalStateException("left footer button pressed, but stage of " +
mUiStage + " doesn't make sense");
}
} else if (v == mFooterRightButton) {
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);
}
saveChosenPatternAndFinish();
} 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 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));
}
}
/**
* 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) {
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);
}
mFooterRightButton.setText(stage.rightMode.text);
mFooterRightButton.setEnabled(stage.rightMode.enabled);
// same for whether the patten 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);
switch (mUiStage) {
case Introduction:
mLockPatternView.clearPattern();
break;
case HelpScreen:
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
break;
case ChoiceTooShort:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case FirstChoiceValid:
break;
case NeedToConfirm:
mLockPatternView.clearPattern();
break;
case ConfirmWrong:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case ChoiceConfirmed:
break;
}
}
// 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 saveChosenPatternAndFinish() {
LockPatternUtils utils = mChooseLockSettingsHelper.utils();
final boolean lockVirgin = !utils.isPatternEverChosen();
utils.saveLockPattern(mChosenPattern);
utils.setLockPatternEnabled(true);
if (lockVirgin) {
utils.setVisiblePatternEnabled(true);
utils.setTactileFeedbackEnabled(false);
}
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
}
}
}

View File

@@ -16,83 +16,100 @@
package com.android.settings;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class ChooseLockPatternExample extends Activity implements View.OnClickListener {
private static final long START_DELAY = 1000;
protected static final String TAG = "Settings";
private View mNextButton;
private View mSkipButton;
private View mImageView;
private AnimationDrawable mAnimation;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
startAnimation(mAnimation);
public class ChooseLockPatternExample extends PreferenceActivity {
// required constructor for fragments
public ChooseLockPatternExample() {
}
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternExampleFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
public static class ChooseLockPatternExampleFragment extends Fragment
implements View.OnClickListener {
private static final long START_DELAY = 1000;
protected static final String TAG = "Settings";
private View mNextButton;
private View mSkipButton;
private View mImageView;
private AnimationDrawable mAnimation;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
startAnimation(mAnimation);
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.choose_lock_pattern_example, null);
mNextButton = view.findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = view.findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
mImageView = (ImageView) view.findViewById(R.id.lock_anim);
mImageView.setBackgroundResource(R.drawable.lock_anim);
mImageView.setOnClickListener(this);
mAnimation = (AnimationDrawable) mImageView.getBackground();
return view;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.choose_lock_pattern_example);
initViews();
}
@Override
public void onResume() {
super.onResume();
mHandler.postDelayed(mRunnable, START_DELAY);
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(mRunnable, START_DELAY);
}
@Override
protected void onPause() {
super.onPause();
stopAnimation(mAnimation);
}
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED);
finish();
} else if (v == mNextButton) {
@Override
public void onPause() {
super.onPause();
stopAnimation(mAnimation);
Intent intent = new Intent(this, ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
finish();
}
}
private void initViews() {
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
mImageView = (ImageView) findViewById(R.id.lock_anim);
mImageView.setBackgroundResource(R.drawable.lock_anim);
mImageView.setOnClickListener(this);
mAnimation = (AnimationDrawable) mImageView.getBackground();
}
protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && !animation.isRunning()) {
animation.run();
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
getActivity().setResult(ChooseLockPattern.RESULT_FINISHED);
getActivity().finish();
} else if (v == mNextButton) {
stopAnimation(mAnimation);
Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
getActivity().finish();
}
}
}
protected void stopAnimation(final AnimationDrawable animation) {
if (animation != null && animation.isRunning()) animation.stop();
protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && !animation.isRunning()) {
animation.run();
}
}
protected void stopAnimation(final AnimationDrawable animation) {
if (animation != null && animation.isRunning()) animation.stop();
}
}
}

View File

@@ -18,49 +18,70 @@ package com.android.settings;
import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ChooseLockPatternTutorial extends Activity implements View.OnClickListener {
private View mNextButton;
private View mSkipButton;
public class ChooseLockPatternTutorial extends PreferenceActivity {
// required constructor for fragments
public ChooseLockPatternTutorial() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't show the tutorial if the user has seen it before.
LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) {
Intent intent = new Intent(this, ChooseLockPattern.class);
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
finish();
} else {
initViews();
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternTutorialFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
public static class ChooseLockPatternTutorialFragment extends Fragment
implements View.OnClickListener {
private View mNextButton;
private View mSkipButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't show the tutorial if the user has seen it before.
LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) {
Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra("confirm_credentials", false);
startActivity(intent);
getActivity().finish();
}
}
}
private void initViews() {
setContentView(R.layout.choose_lock_pattern_tutorial);
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.choose_lock_pattern_tutorial, null);
mNextButton = view.findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = view.findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
return view;
}
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED);
finish();
} else if (v == mNextButton) {
Intent intent = new Intent(this, ChooseLockPatternExample.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
finish();
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
getActivity().setResult(ChooseLockPattern.RESULT_FINISHED);
getActivity().finish();
} else if (v == mNextButton) {
Intent intent = new Intent(getActivity(), ChooseLockPatternExample.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
getActivity().finish();
}
}
}
}

View File

@@ -20,113 +20,140 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.preference.PreferenceActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class ConfirmLockPassword extends Activity implements OnClickListener,
OnEditorActionListener {
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private TextView mPasswordEntry;
private LockPatternUtils mLockPatternUtils;
private TextView mHeaderText;
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
public class ConfirmLockPassword extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(this);
initViews();
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
private void initViews() {
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
setContentView(R.layout.confirm_lock_password);
@Override
public void onCreate(Bundle savedInstanceState) {
// Disable IME on our window since we provide our own keyboard
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
findViewById(R.id.cancel_button).setOnClickListener(this);
findViewById(R.id.next_button).setOnClickListener(this);
mPasswordEntry = (TextView) findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
mHeaderText = (TextView) findViewById(R.id.headerText);
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header
: R.string.lockpassword_confirm_your_pin_header);
mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.requestFocus();
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
//WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
super.onCreate(savedInstanceState);
}
@Override
protected void onPause() {
super.onPause();
mKeyboardView.requestFocus();
}
public static class ConfirmLockPasswordFragment extends Fragment implements OnClickListener,
OnEditorActionListener {
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private TextView mPasswordEntry;
private LockPatternUtils mLockPatternUtils;
private TextView mHeaderText;
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mKeyboardView.requestFocus();
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(pin)) {
setResult(RESULT_OK);
finish();
} else {
showError(R.string.lockpattern_need_to_unlock_wrong);
// required constructor for fragments
public ConfirmLockPasswordFragment() {
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.next_button:
handleNext();
break;
case R.id.cancel_button:
setResult(RESULT_CANCELED);
finish();
break;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getActivity());
}
}
private void showError(int msg) {
mHeaderText.setText(msg);
mPasswordEntry.setText(null);
mHandler.postDelayed(new Runnable() {
public void run() {
mHeaderText.setText(R.string.lockpassword_confirm_your_password_header);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
View view = inflater.inflate(R.layout.confirm_lock_password, null);
// Disable IME on our window since we provide our own keyboard
view.findViewById(R.id.cancel_button).setOnClickListener(this);
view.findViewById(R.id.next_button).setOnClickListener(this);
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header
: R.string.lockpassword_confirm_your_pin_header);
mKeyboardHelper = new PasswordEntryKeyboardHelper(getActivity(),
mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.requestFocus();
return view;
}
@Override
public void onPause() {
super.onPause();
mKeyboardView.requestFocus();
}
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
mKeyboardView.requestFocus();
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(pin)) {
getActivity().setResult(RESULT_OK);
getActivity().finish();
} else {
showError(R.string.lockpattern_need_to_unlock_wrong);
}
}, ERROR_MESSAGE_TIMEOUT);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL) {
handleNext();
return true;
}
return false;
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;
}
}
private void showError(int msg) {
mHeaderText.setText(msg);
mPasswordEntry.setText(null);
mHandler.postDelayed(new Runnable() {
public void run() {
mHeaderText.setText(R.string.lockpassword_confirm_your_password_header);
}
}, ERROR_MESSAGE_TIMEOUT);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL) {
handleNext();
return true;
}
return false;
}
}
}

View File

@@ -22,12 +22,16 @@ import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternView.Cell;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.widget.TextView;
import android.view.Window;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
@@ -37,7 +41,7 @@ import java.util.List;
* Sets an activity result of {@link Activity#RESULT_OK} when the user
* successfully confirmed their pattern.
*/
public class ConfirmLockPattern extends Activity {
public class ConfirmLockPattern extends PreferenceActivity {
/**
* Names of {@link CharSequence} fields within the originating {@link Intent}
@@ -45,30 +49,11 @@ public class ConfirmLockPattern extends Activity {
* The view will use the system-defined resource strings for any labels that
* the caller does not supply.
*/
public static final String HEADER_TEXT = "com.android.settings.ConfirmLockPattern.header";
public static final String FOOTER_TEXT = "com.android.settings.ConfirmLockPattern.footer";
public static final String HEADER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.header_wrong";
public static final String FOOTER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.footer_wrong";
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
private LockPatternView mLockPatternView;
private LockPatternUtils mLockPatternUtils;
private int mNumWrongConfirmAttempts;
private CountDownTimer mCountdownTimer;
private TextView mHeaderTextView;
private TextView mFooterTextView;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
public static final String PACKAGE = "com.android.settings";
public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
public static final String FOOTER_TEXT = PACKAGE + ".ConfirmLockPattern.footer";
public static final String HEADER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.header_wrong";
public static final String FOOTER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.footer_wrong";
private enum Stage {
NeedToUnlock,
@@ -77,194 +62,232 @@ public class ConfirmLockPattern extends Activity {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
mLockPatternUtils = new LockPatternUtils(this);
public static class ConfirmLockPatternFragment extends Fragment {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.confirm_lock_pattern);
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
mHeaderTextView = (TextView) findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mFooterTextView = (TextView) findViewById(R.id.footerText);
private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
private LockPatternView mLockPatternView;
private LockPatternUtils mLockPatternUtils;
private int mNumWrongConfirmAttempts;
private CountDownTimer mCountdownTimer;
private TextView mHeaderTextView;
private TextView mFooterTextView;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
// required constructor for fragments
public ConfirmLockPatternFragment() {
Intent intent = getIntent();
if (intent != null) {
mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
}
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
updateStage(Stage.NeedToUnlock);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getActivity());
}
if (savedInstanceState != null) {
mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
} else {
// 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.savedPatternExists()) {
setResult(RESULT_OK);
finish();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
mFooterTextView = (TextView) view.findViewById(R.id.footerText);
// 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(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// deliberately not calling super since we are managing this in full
outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
}
@Override
protected void onPause() {
super.onPause();
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
}
}
@Override
protected void onResume() {
super.onResume();
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
} else if (!mLockPatternView.isEnabled()) {
// The deadline has passed, but the timer was cancelled...
// Need to clean up.
mNumWrongConfirmAttempts = 0;
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
updateStage(Stage.NeedToUnlock);
}
}
private void updateStage(Stage stage) {
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
}
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
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;
}
}
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);
}
/**
* 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 (mLockPatternUtils.checkPattern(pattern)) {
setResult(RESULT_OK);
finish();
if (savedInstanceState != null) {
mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
updateStage(Stage.NeedToUnlockWrong);
postClearPatternRunnable();
// 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.savedPatternExists()) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}
}
return view;
}
};
@Override
public void onSaveInstanceState(Bundle outState) {
// deliberately not calling super since we are managing this in full
outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
}
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 onPause() {
super.onPause();
@Override
public void onTick(long millisUntilFinished) {
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
}
}
@Override
public void onFinish() {
@Override
public void onResume() {
super.onResume();
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
} else if (!mLockPatternView.isEnabled()) {
// The deadline has passed, but the timer was cancelled...
// Need to clean up.
mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock);
}
}.start();
}
private void updateStage(Stage stage) {
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
}
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
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;
}
}
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);
}
/**
* 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 (mLockPatternUtils.checkPattern(pattern)) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
++mNumWrongConfirmAttempts
>= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
updateStage(Stage.NeedToUnlockWrong);
postClearPatternRunnable();
}
}
}
};
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) {
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
}
@Override
public void onFinish() {
mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock);
}
}.start();
}
}
}

View File

@@ -90,6 +90,8 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
private static final int FALLBACK_LOCK_AFTER_TIMEOUT_VALUE = 5000; // compatible with pre-Froyo
private static final String TAG = "SecuritySettings";
// Credential storage
private final CredentialStorage mCredentialStorage = new CredentialStorage();
@@ -359,8 +361,8 @@ public class SecuritySettings extends SettingsPreferenceFragment
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
Intent intent = new Intent(getActivity(), ChooseLockGeneric.class);
startActivityForResult(intent, SET_OR_CHANGE_LOCK_METHOD_REQUEST);
startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
} else if (KEY_LOCK_ENABLED.equals(key)) {
lockPatternUtils.setLockPatternEnabled(isToggled(preference));
} else if (KEY_VISIBLE_PATTERN.equals(key)) {