ConfirmLockPassword.java revision 3ea423ae0ff56d249b6844b3a68c67ee5eba243d
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings; 18 19import android.text.TextUtils; 20import com.android.internal.widget.LockPatternUtils; 21import com.android.internal.widget.PasswordEntryKeyboardHelper; 22import com.android.internal.widget.PasswordEntryKeyboardView; 23 24import android.app.Activity; 25import android.app.Fragment; 26import android.app.admin.DevicePolicyManager; 27import android.content.Intent; 28import android.os.Bundle; 29import android.os.CountDownTimer; 30import android.os.Handler; 31import android.os.SystemClock; 32import android.os.storage.StorageManager; 33import android.text.Editable; 34import android.text.InputType; 35import android.text.TextWatcher; 36import android.view.KeyEvent; 37import android.view.LayoutInflater; 38import android.view.View; 39import android.view.View.OnClickListener; 40import android.view.ViewGroup; 41import android.view.inputmethod.EditorInfo; 42import android.widget.Button; 43import android.widget.TextView; 44import android.widget.TextView.OnEditorActionListener; 45 46public class ConfirmLockPassword extends SettingsActivity { 47 48 public static final String PACKAGE = "com.android.settings"; 49 public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header"; 50 51 public static class InternalActivity extends ConfirmLockPassword { 52 } 53 54 @Override 55 public Intent getIntent() { 56 Intent modIntent = new Intent(super.getIntent()); 57 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName()); 58 return modIntent; 59 } 60 61 @Override 62 protected boolean isValidFragment(String fragmentName) { 63 if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true; 64 return false; 65 } 66 67 @Override 68 public void onCreate(Bundle savedInstanceState) { 69 // Disable IME on our window since we provide our own keyboard 70 //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 71 //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 72 super.onCreate(savedInstanceState); 73 CharSequence msg = getText(R.string.lockpassword_confirm_your_password_header); 74 setTitle(msg); 75 } 76 77 public static class ConfirmLockPasswordFragment extends Fragment implements OnClickListener, 78 OnEditorActionListener, TextWatcher { 79 private static final String KEY_NUM_WRONG_CONFIRM_ATTEMPTS 80 = "confirm_lock_password_fragment.key_num_wrong_confirm_attempts"; 81 private static final long ERROR_MESSAGE_TIMEOUT = 3000; 82 private TextView mPasswordEntry; 83 private LockPatternUtils mLockPatternUtils; 84 private TextView mHeaderText; 85 private Handler mHandler = new Handler(); 86 private PasswordEntryKeyboardHelper mKeyboardHelper; 87 private PasswordEntryKeyboardView mKeyboardView; 88 private Button mContinueButton; 89 private int mNumWrongConfirmAttempts; 90 private CountDownTimer mCountdownTimer; 91 private boolean mIsAlpha; 92 93 // required constructor for fragments 94 public ConfirmLockPasswordFragment() { 95 96 } 97 98 @Override 99 public void onCreate(Bundle savedInstanceState) { 100 super.onCreate(savedInstanceState); 101 mLockPatternUtils = new LockPatternUtils(getActivity()); 102 if (savedInstanceState != null) { 103 mNumWrongConfirmAttempts = savedInstanceState.getInt( 104 KEY_NUM_WRONG_CONFIRM_ATTEMPTS, 0); 105 } 106 } 107 108 @Override 109 public View onCreateView(LayoutInflater inflater, ViewGroup container, 110 Bundle savedInstanceState) { 111 final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(); 112 View view = inflater.inflate(R.layout.confirm_lock_password, null); 113 // Disable IME on our window since we provide our own keyboard 114 115 view.findViewById(R.id.cancel_button).setOnClickListener(this); 116 mContinueButton = (Button) view.findViewById(R.id.next_button); 117 mContinueButton.setOnClickListener(this); 118 mContinueButton.setEnabled(false); // disable until the user enters at least one char 119 120 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry); 121 mPasswordEntry.setOnEditorActionListener(this); 122 mPasswordEntry.addTextChangedListener(this); 123 124 mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard); 125 mHeaderText = (TextView) view.findViewById(R.id.headerText); 126 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality 127 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality 128 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality; 129 130 Intent intent = getActivity().getIntent(); 131 if (intent != null) { 132 CharSequence headerMessage = intent.getCharSequenceExtra(HEADER_TEXT); 133 if (TextUtils.isEmpty(headerMessage)) { 134 headerMessage = getString(getDefaultHeader()); 135 } 136 mHeaderText.setText(headerMessage); 137 } 138 139 final Activity activity = getActivity(); 140 mKeyboardHelper = new PasswordEntryKeyboardHelper(activity, 141 mKeyboardView, mPasswordEntry); 142 mKeyboardHelper.setKeyboardMode(mIsAlpha ? 143 PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA 144 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); 145 mKeyboardView.requestFocus(); 146 147 int currentType = mPasswordEntry.getInputType(); 148 mPasswordEntry.setInputType(mIsAlpha ? currentType 149 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD)); 150 151 if (activity instanceof SettingsActivity) { 152 final SettingsActivity sa = (SettingsActivity) activity; 153 int id = getDefaultHeader(); 154 CharSequence title = getText(id); 155 sa.setTitle(title); 156 } 157 158 return view; 159 } 160 161 private int getDefaultHeader() { 162 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header 163 : R.string.lockpassword_confirm_your_pin_header; 164 } 165 166 @Override 167 public void onPause() { 168 super.onPause(); 169 mKeyboardView.requestFocus(); 170 if (mCountdownTimer != null) { 171 mCountdownTimer.cancel(); 172 mCountdownTimer = null; 173 } 174 } 175 176 @Override 177 public void onResume() { 178 // TODO Auto-generated method stub 179 super.onResume(); 180 mKeyboardView.requestFocus(); 181 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); 182 if (deadline != 0) { 183 handleAttemptLockout(deadline); 184 } 185 } 186 187 @Override 188 public void onSaveInstanceState(Bundle outState) { 189 super.onSaveInstanceState(outState); 190 outState.putInt(KEY_NUM_WRONG_CONFIRM_ATTEMPTS, mNumWrongConfirmAttempts); 191 } 192 193 private void handleNext() { 194 final String pin = mPasswordEntry.getText().toString(); 195 if (mLockPatternUtils.checkPassword(pin)) { 196 197 Intent intent = new Intent(); 198 if (getActivity() instanceof ConfirmLockPassword.InternalActivity) { 199 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE, 200 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD 201 : StorageManager.CRYPT_TYPE_PIN); 202 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin); 203 } 204 205 getActivity().setResult(RESULT_OK, intent); 206 getActivity().finish(); 207 } else { 208 if (++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { 209 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); 210 handleAttemptLockout(deadline); 211 } else { 212 showError(R.string.lockpattern_need_to_unlock_wrong); 213 } 214 } 215 } 216 217 private void handleAttemptLockout(long elapsedRealtimeDeadline) { 218 long elapsedRealtime = SystemClock.elapsedRealtime(); 219 showError(R.string.lockpattern_too_many_failed_confirmation_attempts_header, 0); 220 mPasswordEntry.setEnabled(false); 221 mCountdownTimer = new CountDownTimer( 222 elapsedRealtimeDeadline - elapsedRealtime, 223 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) { 224 225 @Override 226 public void onTick(long millisUntilFinished) { 227 final int secondsCountdown = (int) (millisUntilFinished / 1000); 228 mHeaderText.setText(getString( 229 R.string.lockpattern_too_many_failed_confirmation_attempts_footer, 230 secondsCountdown)); 231 } 232 233 @Override 234 public void onFinish() { 235 mPasswordEntry.setEnabled(true); 236 mHeaderText.setText(getDefaultHeader()); 237 mNumWrongConfirmAttempts = 0; 238 } 239 }.start(); 240 } 241 242 public void onClick(View v) { 243 switch (v.getId()) { 244 case R.id.next_button: 245 handleNext(); 246 break; 247 248 case R.id.cancel_button: 249 getActivity().setResult(RESULT_CANCELED); 250 getActivity().finish(); 251 break; 252 } 253 } 254 255 private void showError(int msg) { 256 showError(msg, ERROR_MESSAGE_TIMEOUT); 257 } 258 259 private final Runnable mResetErrorRunnable = new Runnable() { 260 public void run() { 261 mHeaderText.setText(getDefaultHeader()); 262 } 263 }; 264 265 private void showError(int msg, long timeout) { 266 mHeaderText.setText(msg); 267 mHeaderText.announceForAccessibility(mHeaderText.getText()); 268 mPasswordEntry.setText(null); 269 mHandler.removeCallbacks(mResetErrorRunnable); 270 if (timeout != 0) { 271 mHandler.postDelayed(mResetErrorRunnable, timeout); 272 } 273 } 274 275 // {@link OnEditorActionListener} methods. 276 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 277 // Check if this was the result of hitting the enter or "done" key 278 if (actionId == EditorInfo.IME_NULL 279 || actionId == EditorInfo.IME_ACTION_DONE 280 || actionId == EditorInfo.IME_ACTION_NEXT) { 281 handleNext(); 282 return true; 283 } 284 return false; 285 } 286 287 // {@link TextWatcher} methods. 288 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 289 } 290 291 public void onTextChanged(CharSequence s, int start, int before, int count) { 292 } 293 294 public void afterTextChanged(Editable s) { 295 mContinueButton.setEnabled(mPasswordEntry.getText().length() > 0); 296 } 297 } 298} 299