blob: a4e8ea400cf3e9dbaf45c787cdf75f5225925c4a [file] [log] [blame]
Jim Miller5e0f7ba2009-12-22 19:04:23 -08001/*
Jim Millerdcb3d842012-08-23 19:18:12 -07002 * Copyright (C) 2012 The Android Open Source Project
Jim Miller5e0f7ba2009-12-22 19:04:23 -08003 *
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
Jim Millerdcb3d842012-08-23 19:18:12 -070017package com.android.internal.policy.impl.keyguard;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080018
Jim Millerdcb3d842012-08-23 19:18:12 -070019import android.content.Context;
20import android.util.AttributeSet;
21import android.view.View;
22
23import com.android.internal.R;
24import com.android.internal.widget.LockPatternUtils;
Jim Miller858f8ea2011-08-19 14:50:34 -070025import java.util.List;
26
Jim Millerf7c5d0e2010-03-25 18:37:48 -070027import android.app.admin.DevicePolicyManager;
Jim Miller54759062010-03-11 15:46:29 -080028import android.content.res.Configuration;
Jim Miller148e73e2010-02-11 15:39:45 -080029import android.graphics.Rect;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080030
Jim Millera781d012010-02-05 18:59:25 -080031import com.android.internal.widget.PasswordEntryKeyboardView;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080032
Jim Millerf1818ff2010-03-17 19:14:18 -070033import android.os.CountDownTimer;
34import android.os.SystemClock;
Jim Miller8ecfac12011-07-08 19:15:51 -070035import android.text.Editable;
Jim Millerda3ae8f2011-04-14 15:13:49 -070036import android.text.InputType;
Jim Miller8ecfac12011-07-08 19:15:51 -070037import android.text.TextWatcher;
Jim Miller8b9dda22010-03-02 17:24:48 -080038import android.text.method.DigitsKeyListener;
39import android.text.method.TextKeyListener;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080040import android.view.KeyEvent;
Jim Millera781d012010-02-05 18:59:25 -080041import android.view.inputmethod.EditorInfo;
Jim Miller858f8ea2011-08-19 14:50:34 -070042import android.view.inputmethod.InputMethodInfo;
43import android.view.inputmethod.InputMethodManager;
44import android.view.inputmethod.InputMethodSubtype;
Jim Millera781d012010-02-05 18:59:25 -080045import android.widget.EditText;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080046import android.widget.LinearLayout;
47import android.widget.TextView;
Jim Millera781d012010-02-05 18:59:25 -080048import android.widget.TextView.OnEditorActionListener;
49
Jim Millera781d012010-02-05 18:59:25 -080050import com.android.internal.widget.PasswordEntryKeyboardHelper;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080051/**
52 * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
53 * an unlock password
54 */
Jim Miller5e0f7ba2009-12-22 19:04:23 -080055
Jim Millerdcb3d842012-08-23 19:18:12 -070056public class KeyguardPasswordView extends LinearLayout
Jim Miller9cf2c522012-10-04 22:02:29 -070057 implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
Jim Millerdcb3d842012-08-23 19:18:12 -070058 private KeyguardSecurityCallback mCallback;
59 private EditText mPasswordEntry;
60 private LockPatternUtils mLockPatternUtils;
61 private PasswordEntryKeyboardView mKeyboardView;
62 private PasswordEntryKeyboardHelper mKeyboardHelper;
63 private boolean mIsAlpha;
64 private KeyguardNavigationManager mNavigationManager;
Jim Miller54759062010-03-11 15:46:29 -080065
Jim Miller6fe0f0e2010-01-27 17:53:18 -080066 // To avoid accidental lockout due to events while the device in in the pocket, ignore
67 // any passwords with length less than or equal to this length.
68 private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
69
Jim Millerac8f5752012-09-07 16:48:41 -070070 // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
71 private static final boolean ENABLE_HIDE_KEYBOARD = false;
72
Jim Millerdcb3d842012-08-23 19:18:12 -070073 public KeyguardPasswordView(Context context) {
Jim Miller5e0f7ba2009-12-22 19:04:23 -080074 super(context);
Jim Millerdcb3d842012-08-23 19:18:12 -070075 }
Jim Miller54759062010-03-11 15:46:29 -080076
Jim Millerdcb3d842012-08-23 19:18:12 -070077 public KeyguardPasswordView(Context context, AttributeSet attrs) {
78 super(context, attrs);
79 }
80
81 public void setKeyguardCallback(KeyguardSecurityCallback callback) {
Jim Miller5e0f7ba2009-12-22 19:04:23 -080082 mCallback = callback;
Jim Millerdcb3d842012-08-23 19:18:12 -070083 }
Jim Miller5e0f7ba2009-12-22 19:04:23 -080084
Jim Millerdcb3d842012-08-23 19:18:12 -070085 public void setLockPatternUtils(LockPatternUtils utils) {
86 mLockPatternUtils = utils;
87 }
88
89 public void reset() {
90 // start fresh
91 mPasswordEntry.setText("");
92 mPasswordEntry.requestFocus();
93
94 // if the user is currently locked out, enforce it.
95 long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
96 if (deadline != 0) {
97 handleAttemptLockout(deadline);
Jim Miller42b432d2010-02-12 18:56:03 -080098 } else {
Jim Miller08b2b6b2012-09-14 19:12:40 -070099 resetState();
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800100 }
Jim Millerdcb3d842012-08-23 19:18:12 -0700101 }
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800102
Jim Miller08b2b6b2012-09-14 19:12:40 -0700103 private void resetState() {
104 mNavigationManager.setMessage(
105 mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions);
106 mPasswordEntry.setEnabled(true);
107 mKeyboardView.setEnabled(true);
108 }
109
Jim Millerdcb3d842012-08-23 19:18:12 -0700110 @Override
111 protected void onFinishInflate() {
112 mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one
Jae Yong Sung8171b512010-08-05 10:44:27 -0700113
Jim Millerdcb3d842012-08-23 19:18:12 -0700114 mNavigationManager = new KeyguardNavigationManager(this);
115
116 final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
Konstantin Lopyrevf9a166a2010-06-01 16:47:05 -0700117 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700118 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
119 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
Jim Millerf7c5d0e2010-03-25 18:37:48 -0700120
Jim Millera781d012010-02-05 18:59:25 -0800121 mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
122 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
123 mPasswordEntry.setOnEditorActionListener(this);
Jim Miller9cf2c522012-10-04 22:02:29 -0700124 mPasswordEntry.addTextChangedListener(this);
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800125
Jim Miller3af630c2012-09-26 14:29:18 -0700126 mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false,
127 new int[] {
128 R.xml.kg_password_kbd_numeric,
129 com.android.internal.R.xml.password_kbd_qwerty,
130 com.android.internal.R.xml.password_kbd_qwerty_shifted,
131 com.android.internal.R.xml.password_kbd_symbols,
132 com.android.internal.R.xml.password_kbd_symbols_shift
133 }
134 );
Jeff Sharkey3fb0af32012-05-15 12:37:33 -0700135 mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
Jim Millerdcb3d842012-08-23 19:18:12 -0700136
Jim Millerd9d09452011-11-02 18:32:16 -0700137 boolean imeOrDeleteButtonVisible = false;
Jim Miller6b05d582011-07-18 13:09:59 -0700138 if (mIsAlpha) {
139 // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
140 mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
Jae Yong Sung8171b512010-08-05 10:44:27 -0700141 mKeyboardView.setVisibility(View.GONE);
Jim Miller6b05d582011-07-18 13:09:59 -0700142 } else {
Jim Miller6b05d582011-07-18 13:09:59 -0700143 mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
Jim Millerac8f5752012-09-07 16:48:41 -0700144
145 // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
146 boolean hardKeyboardVisible = getResources().getConfiguration().hardKeyboardHidden
147 == Configuration.HARDKEYBOARDHIDDEN_NO;
148 mKeyboardView.setVisibility(
149 (ENABLE_HIDE_KEYBOARD && hardKeyboardVisible) ? View.INVISIBLE : View.VISIBLE);
Ben Komalo51ea88a2011-10-03 10:53:26 -0700150
151 // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
152 // not a separate view
Jim Millerdcb3d842012-08-23 19:18:12 -0700153 View pinDelete = findViewById(R.id.delete_button);
Ben Komalo51ea88a2011-10-03 10:53:26 -0700154 if (pinDelete != null) {
155 pinDelete.setVisibility(View.VISIBLE);
Jim Millerd9d09452011-11-02 18:32:16 -0700156 imeOrDeleteButtonVisible = true;
Ben Komalo51ea88a2011-10-03 10:53:26 -0700157 pinDelete.setOnClickListener(new OnClickListener() {
Ben Komalo51ea88a2011-10-03 10:53:26 -0700158 public void onClick(View v) {
159 mKeyboardHelper.handleBackspace();
160 }
161 });
162 }
Jae Yong Sung8171b512010-08-05 10:44:27 -0700163 }
Jim Millera781d012010-02-05 18:59:25 -0800164
Jim Millera781d012010-02-05 18:59:25 -0800165 mPasswordEntry.requestFocus();
Jim Miller966b1262010-02-18 19:55:29 -0800166
Jim Miller8ecfac12011-07-08 19:15:51 -0700167 // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys.
Konstantin Lopyrevf9a166a2010-06-01 16:47:05 -0700168 if (mIsAlpha) {
Jim Miller8b9dda22010-03-02 17:24:48 -0800169 mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
Jim Miller8ecfac12011-07-08 19:15:51 -0700170 mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
171 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
Jim Miller8b9dda22010-03-02 17:24:48 -0800172 } else {
173 mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
Jim Miller8ecfac12011-07-08 19:15:51 -0700174 mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
175 | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
Jim Miller8b9dda22010-03-02 17:24:48 -0800176 }
177
Jim Miller6b05d582011-07-18 13:09:59 -0700178 // Poke the wakelock any time the text is selected or modified
179 mPasswordEntry.setOnClickListener(new OnClickListener() {
180 public void onClick(View v) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700181 mCallback.userActivity(0); // TODO: customize timeout for text?
Jim Miller6b05d582011-07-18 13:09:59 -0700182 }
183 });
Jim Millerdcb3d842012-08-23 19:18:12 -0700184
Jim Miller8ecfac12011-07-08 19:15:51 -0700185 mPasswordEntry.addTextChangedListener(new TextWatcher() {
186 public void onTextChanged(CharSequence s, int start, int before, int count) {
187 }
188
189 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
190 }
191
192 public void afterTextChanged(Editable s) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700193 mCallback.userActivity(0);
Jim Miller8ecfac12011-07-08 19:15:51 -0700194 }
195 });
Jim Miller858f8ea2011-08-19 14:50:34 -0700196
197 // If there's more than one IME, enable the IME switcher button
198 View switchImeButton = findViewById(R.id.switch_ime_button);
199 final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
200 Context.INPUT_METHOD_SERVICE);
Jim Millerb0166bc2011-08-28 15:00:29 -0700201 if (mIsAlpha && switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
Jim Miller858f8ea2011-08-19 14:50:34 -0700202 switchImeButton.setVisibility(View.VISIBLE);
Jim Millerd9d09452011-11-02 18:32:16 -0700203 imeOrDeleteButtonVisible = true;
Jim Miller858f8ea2011-08-19 14:50:34 -0700204 switchImeButton.setOnClickListener(new OnClickListener() {
205 public void onClick(View v) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700206 mCallback.userActivity(0); // Leave the screen on a bit longer
Jim Miller858f8ea2011-08-19 14:50:34 -0700207 imm.showInputMethodPicker();
208 }
209 });
210 }
Jim Millerd9d09452011-11-02 18:32:16 -0700211
212 // If no icon is visible, reset the left margin on the password field so the text is
213 // still centered.
214 if (!imeOrDeleteButtonVisible) {
215 android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
216 if (params instanceof MarginLayoutParams) {
217 ((MarginLayoutParams)params).leftMargin = 0;
218 mPasswordEntry.setLayoutParams(params);
219 }
220 }
Jim Miller858f8ea2011-08-19 14:50:34 -0700221 }
222
223 /**
224 * Method adapted from com.android.inputmethod.latin.Utils
225 *
226 * @param imm The input method manager
227 * @param shouldIncludeAuxiliarySubtypes
228 * @return true if we have multiple IMEs to choose from
229 */
230 private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
231 final boolean shouldIncludeAuxiliarySubtypes) {
232 final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
233
234 // Number of the filtered IMEs
235 int filteredImisCount = 0;
236
237 for (InputMethodInfo imi : enabledImis) {
238 // We can return true immediately after we find two or more filtered IMEs.
239 if (filteredImisCount > 1) return true;
240 final List<InputMethodSubtype> subtypes =
241 imm.getEnabledInputMethodSubtypeList(imi, true);
242 // IMEs that have no subtypes should be counted.
243 if (subtypes.isEmpty()) {
244 ++filteredImisCount;
245 continue;
246 }
247
248 int auxCount = 0;
249 for (InputMethodSubtype subtype : subtypes) {
250 if (subtype.isAuxiliary()) {
251 ++auxCount;
252 }
253 }
254 final int nonAuxCount = subtypes.size() - auxCount;
255
256 // IMEs that have one or more non-auxiliary subtypes should be counted.
257 // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
258 // subtypes should be counted as well.
259 if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
260 ++filteredImisCount;
261 continue;
262 }
263 }
264
265 return filteredImisCount > 1
266 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
267 // input method subtype (The current IME should be LatinIME.)
268 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800269 }
270
Jim Miller148e73e2010-02-11 15:39:45 -0800271 @Override
272 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
273 // send focus to the password field
274 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
275 }
276
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800277 private void verifyPasswordAndUnlock() {
Jim Millera781d012010-02-05 18:59:25 -0800278 String entry = mPasswordEntry.getText().toString();
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800279 if (mLockPatternUtils.checkPassword(entry)) {
Jim Miller6fe0f0e2010-01-27 17:53:18 -0800280 mCallback.reportSuccessfulUnlockAttempt();
Jim Millerdcb3d842012-08-23 19:18:12 -0700281 mCallback.dismiss(true);
Jim Miller6fe0f0e2010-01-27 17:53:18 -0800282 } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800283 // to avoid accidental lockout, only count attempts that are long enough to be a
284 // real password. This may require some tweaking.
Jim Miller6fe0f0e2010-01-27 17:53:18 -0800285 mCallback.reportFailedUnlockAttempt();
Jim Millerdcb3d842012-08-23 19:18:12 -0700286 if (0 == (mCallback.getFailedAttempts()
Jim Millerf1818ff2010-03-17 19:14:18 -0700287 % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
288 long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
289 handleAttemptLockout(deadline);
290 }
Jim Millerd36ebe02012-08-31 17:42:42 -0700291 mNavigationManager.setMessage(
292 mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin);
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800293 }
Jim Millera781d012010-02-05 18:59:25 -0800294 mPasswordEntry.setText("");
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800295 }
296
Jim Millerf1818ff2010-03-17 19:14:18 -0700297 // Prevent user from using the PIN/Password entry until scheduled deadline.
298 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
299 mPasswordEntry.setEnabled(false);
300 mKeyboardView.setEnabled(false);
301 long elapsedRealtime = SystemClock.elapsedRealtime();
Ben Komalo51ea88a2011-10-03 10:53:26 -0700302 new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
Jim Millerf1818ff2010-03-17 19:14:18 -0700303
304 @Override
305 public void onTick(long millisUntilFinished) {
306 int secondsRemaining = (int) (millisUntilFinished / 1000);
Jim Millerdcb3d842012-08-23 19:18:12 -0700307 mNavigationManager.setMessage(
308 R.string.kg_too_many_failed_attempts_countdown, secondsRemaining);
Jim Millerf1818ff2010-03-17 19:14:18 -0700309 }
310
311 @Override
312 public void onFinish() {
Jim Miller08b2b6b2012-09-14 19:12:40 -0700313 resetState();
Jim Millerf1818ff2010-03-17 19:14:18 -0700314 }
315 }.start();
316 }
317
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800318 @Override
319 public boolean onKeyDown(int keyCode, KeyEvent event) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700320 mCallback.userActivity(0);
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800321 return false;
322 }
323
Dianne Hackborn75787392010-03-18 01:04:37 -0700324 @Override
Jim Millera781d012010-02-05 18:59:25 -0800325 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
326 // Check if this was the result of hitting the enter key
Jim Miller1c188282011-08-08 22:27:58 -0700327 if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
328 || actionId == EditorInfo.IME_ACTION_NEXT) {
Jim Millera781d012010-02-05 18:59:25 -0800329 verifyPasswordAndUnlock();
330 return true;
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800331 }
Jim Millera781d012010-02-05 18:59:25 -0800332 return false;
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800333 }
Jim Millerdcb3d842012-08-23 19:18:12 -0700334
335 @Override
336 public boolean needsInput() {
337 return mIsAlpha;
338 }
339
340 @Override
341 public void onPause() {
342
343 }
344
345 @Override
346 public void onResume() {
347 reset();
348 }
349
350 @Override
351 public KeyguardSecurityCallback getCallback() {
352 return mCallback;
353 }
354
Jim Miller9cf2c522012-10-04 22:02:29 -0700355 @Override
356 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
357 if (mCallback != null) {
358 mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
359 }
360 }
361
362 @Override
363 public void onTextChanged(CharSequence s, int start, int before, int count) {
364 }
365
366 @Override
367 public void afterTextChanged(Editable s) {
368 }
369
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800370}
Jim Millerdcb3d842012-08-23 19:18:12 -0700371