1 /* 2 * Copyright (C) 2012 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 17 package com.android.keyguard; 18 19 import android.content.Context; 20 import android.graphics.Rect; 21 import android.graphics.drawable.Drawable; 22 import android.os.CountDownTimer; 23 import android.os.SystemClock; 24 import android.text.Editable; 25 import android.text.TextWatcher; 26 import android.util.AttributeSet; 27 import android.view.HapticFeedbackConstants; 28 import android.view.KeyEvent; 29 import android.view.View; 30 import android.view.inputmethod.EditorInfo; 31 import android.widget.LinearLayout; 32 import android.widget.TextView; 33 import android.widget.TextView.OnEditorActionListener; 34 35 import com.android.internal.widget.LockPatternUtils; 36 37 /** 38 * Base class for PIN and password unlock screens. 39 */ 40 public abstract class KeyguardAbsKeyInputView extends LinearLayout 41 implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { 42 protected KeyguardSecurityCallback mCallback; 43 protected TextView mPasswordEntry; 44 protected LockPatternUtils mLockPatternUtils; 45 protected SecurityMessageDisplay mSecurityMessageDisplay; 46 protected View mEcaView; 47 private Drawable mBouncerFrame; 48 protected boolean mEnableHaptics; 49 50 // To avoid accidental lockout due to events while the device in in the pocket, ignore 51 // any passwords with length less than or equal to this length. 52 protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; 53 54 public KeyguardAbsKeyInputView(Context context) { 55 this(context, null); 56 } 57 58 public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 63 mCallback = callback; 64 } 65 66 public void setLockPatternUtils(LockPatternUtils utils) { 67 mLockPatternUtils = utils; 68 mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); 69 } 70 71 @Override 72 public void onWindowFocusChanged(boolean hasWindowFocus) { 73 if (hasWindowFocus) { 74 reset(); 75 } 76 } 77 78 public void reset() { 79 // start fresh 80 mPasswordEntry.setText(""); 81 mPasswordEntry.requestFocus(); 82 83 // if the user is currently locked out, enforce it. 84 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); 85 if (shouldLockout(deadline)) { 86 handleAttemptLockout(deadline); 87 } else { 88 resetState(); 89 } 90 } 91 92 // Allow subclasses to override this behavior 93 protected boolean shouldLockout(long deadline) { 94 return deadline != 0; 95 } 96 97 protected abstract int getPasswordTextViewId(); 98 protected abstract void resetState(); 99 100 @Override 101 protected void onFinishInflate() { 102 mLockPatternUtils = new LockPatternUtils(mContext); 103 104 mPasswordEntry = (TextView) findViewById(getPasswordTextViewId()); 105 mPasswordEntry.setOnEditorActionListener(this); 106 mPasswordEntry.addTextChangedListener(this); 107 108 // Set selected property on so the view can send accessibility events. 109 mPasswordEntry.setSelected(true); 110 111 // Poke the wakelock any time the text is selected or modified 112 mPasswordEntry.setOnClickListener(new OnClickListener() { 113 public void onClick(View v) { 114 mCallback.userActivity(0); // TODO: customize timeout for text? 115 } 116 }); 117 118 mPasswordEntry.addTextChangedListener(new TextWatcher() { 119 public void onTextChanged(CharSequence s, int start, int before, int count) { 120 } 121 122 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 123 } 124 125 public void afterTextChanged(Editable s) { 126 if (mCallback != null) { 127 mCallback.userActivity(0); 128 } 129 } 130 }); 131 mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); 132 mEcaView = findViewById(R.id.keyguard_selector_fade_container); 133 View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); 134 if (bouncerFrameView != null) { 135 mBouncerFrame = bouncerFrameView.getBackground(); 136 } 137 } 138 139 @Override 140 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 141 // send focus to the password field 142 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); 143 } 144 145 /* 146 * Override this if you have a different string for "wrong password" 147 * 148 * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this 149 */ 150 protected int getWrongPasswordStringId() { 151 return R.string.kg_wrong_password; 152 } 153 154 protected void verifyPasswordAndUnlock() { 155 String entry = mPasswordEntry.getText().toString(); 156 if (mLockPatternUtils.checkPassword(entry)) { 157 mCallback.reportSuccessfulUnlockAttempt(); 158 mCallback.dismiss(true); 159 } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) { 160 // to avoid accidental lockout, only count attempts that are long enough to be a 161 // real password. This may require some tweaking. 162 mCallback.reportFailedUnlockAttempt(); 163 if (0 == (mCallback.getFailedAttempts() 164 % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { 165 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); 166 handleAttemptLockout(deadline); 167 } 168 mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); 169 } 170 mPasswordEntry.setText(""); 171 } 172 173 // Prevent user from using the PIN/Password entry until scheduled deadline. 174 protected void handleAttemptLockout(long elapsedRealtimeDeadline) { 175 mPasswordEntry.setEnabled(false); 176 long elapsedRealtime = SystemClock.elapsedRealtime(); 177 new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { 178 179 @Override 180 public void onTick(long millisUntilFinished) { 181 int secondsRemaining = (int) (millisUntilFinished / 1000); 182 mSecurityMessageDisplay.setMessage( 183 R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining); 184 } 185 186 @Override 187 public void onFinish() { 188 mSecurityMessageDisplay.setMessage("", false); 189 resetState(); 190 } 191 }.start(); 192 } 193 194 @Override 195 public boolean onKeyDown(int keyCode, KeyEvent event) { 196 mCallback.userActivity(0); 197 return false; 198 } 199 200 @Override 201 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 202 // Check if this was the result of hitting the enter key 203 if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE 204 || actionId == EditorInfo.IME_ACTION_NEXT) { 205 verifyPasswordAndUnlock(); 206 return true; 207 } 208 return false; 209 } 210 211 @Override 212 public boolean needsInput() { 213 return false; 214 } 215 216 @Override 217 public void onPause() { 218 219 } 220 221 @Override 222 public void onResume(int reason) { 223 reset(); 224 } 225 226 @Override 227 public KeyguardSecurityCallback getCallback() { 228 return mCallback; 229 } 230 231 @Override 232 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 233 if (mCallback != null) { 234 mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); 235 } 236 } 237 238 @Override 239 public void onTextChanged(CharSequence s, int start, int before, int count) { 240 } 241 242 @Override 243 public void afterTextChanged(Editable s) { 244 } 245 246 // Cause a VIRTUAL_KEY vibration 247 public void doHapticKeyClick() { 248 if (mEnableHaptics) { 249 performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, 250 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 251 | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); 252 } 253 } 254 255 @Override 256 public void showBouncer(int duration) { 257 KeyguardSecurityViewHelper. 258 showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); 259 } 260 261 @Override 262 public void hideBouncer(int duration) { 263 KeyguardSecurityViewHelper. 264 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); 265 } 266 } 267 268