PasswordUnlockScreen.java revision 305093f9116b364856bcdcd499e9f69910e249dd
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.internal.policy.impl; 18 19import android.app.admin.DevicePolicyManager; 20import android.content.Context; 21import android.content.res.Configuration; 22import android.graphics.Rect; 23 24import com.android.internal.policy.impl.PatternUnlockScreen.FooterMode; 25import com.android.internal.widget.LockPatternUtils; 26import com.android.internal.widget.PasswordEntryKeyboardView; 27 28import android.os.CountDownTimer; 29import android.os.SystemClock; 30import android.telephony.TelephonyManager; 31import android.text.method.DigitsKeyListener; 32import android.text.method.TextKeyListener; 33import android.util.Log; 34import android.view.KeyEvent; 35import android.view.LayoutInflater; 36import android.view.View; 37import android.view.inputmethod.EditorInfo; 38import android.widget.Button; 39import android.widget.EditText; 40import android.widget.LinearLayout; 41import android.widget.TextView; 42import android.widget.TextView.OnEditorActionListener; 43 44import com.android.internal.R; 45import com.android.internal.widget.PasswordEntryKeyboardHelper; 46 47/** 48 * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter 49 * an unlock password 50 */ 51public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen, 52 View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener { 53 54 private static final String TAG = "PasswordUnlockScreen"; 55 private final KeyguardUpdateMonitor mUpdateMonitor; 56 private final KeyguardScreenCallback mCallback; 57 58 private boolean mIsAlpha; 59 60 private EditText mPasswordEntry; 61 private Button mEmergencyCallButton; 62 private LockPatternUtils mLockPatternUtils; 63 private PasswordEntryKeyboardView mKeyboardView; 64 private PasswordEntryKeyboardView mKeyboardViewAlpha; 65 private PasswordEntryKeyboardHelper mKeyboardHelper; 66 private PasswordEntryKeyboardHelper mKeyboardHelperAlpha; 67 68 private int mCreationOrientation; 69 private int mCreationHardKeyboardHidden; 70 private CountDownTimer mCountdownTimer; 71 72 private StatusView mStatusView; 73 74 // To avoid accidental lockout due to events while the device in in the pocket, ignore 75 // any passwords with length less than or equal to this length. 76 private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; 77 78 public PasswordUnlockScreen(Context context, Configuration configuration, 79 LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor, 80 KeyguardScreenCallback callback) { 81 super(context); 82 83 mCreationHardKeyboardHidden = configuration.hardKeyboardHidden; 84 mCreationOrientation = configuration.orientation; 85 mUpdateMonitor = updateMonitor; 86 mCallback = callback; 87 mLockPatternUtils = lockPatternUtils; 88 89 LayoutInflater layoutInflater = LayoutInflater.from(context); 90 if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) { 91 layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true); 92 } else { 93 layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true); 94 } 95 96 mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils); 97 98 final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality(); 99 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality 100 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality 101 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality; 102 103 mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); 104 mKeyboardViewAlpha = (PasswordEntryKeyboardView) findViewById(R.id.keyboardAlpha); 105 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); 106 mPasswordEntry.setOnEditorActionListener(this); 107 mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall); 108 mEmergencyCallButton.setOnClickListener(this); 109 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 110 111 mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false); 112 // TODO: re-enable on phones with keyboards 113 boolean isPhysicalKbShowing = false; 114 //mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; 115 if (mKeyboardViewAlpha == null || !mIsAlpha) { 116 mKeyboardHelper.setKeyboardMode(mIsAlpha ? 117 PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA 118 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); 119 mKeyboardView.setVisibility(isPhysicalKbShowing ? View.INVISIBLE : View.VISIBLE); 120 } else { 121 mKeyboardHelperAlpha = new PasswordEntryKeyboardHelper(context, mKeyboardViewAlpha, 122 this, false); 123 mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); 124 mKeyboardHelperAlpha.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA); 125 mKeyboardView.setVisibility(View.GONE); 126 mKeyboardViewAlpha.setVisibility(isPhysicalKbShowing ? View.INVISIBLE : View.VISIBLE); 127 mPasswordEntry.setWidth(mKeyboardViewAlpha.getLayoutParams().width); 128 } 129 130 mPasswordEntry.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 131 0, 0); 132 mPasswordEntry.requestFocus(); 133 134 // This allows keyboards with overlapping qwerty/numeric keys to choose just the 135 // numeric keys. 136 if (mIsAlpha) { 137 mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); 138 mStatusView.setHelpMessage(R.string.keyguard_password_enter_password_code, 139 StatusView.LOCK_ICON); 140 } else { 141 mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); 142 mStatusView.setHelpMessage(R.string.keyguard_password_enter_pin_code, 143 StatusView.LOCK_ICON); 144 } 145 146 mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ? 147 com.android.internal.R.array.config_virtualKeyVibePattern : 0); 148 if (mKeyboardHelperAlpha != null) { 149 mKeyboardHelperAlpha.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ? 150 com.android.internal.R.array.config_virtualKeyVibePattern : 0); 151 } 152 153 // until we get an update... 154 mStatusView.setCarrierText(LockScreen.getCarrierString( 155 mUpdateMonitor.getTelephonyPlmn(), 156 mUpdateMonitor.getTelephonySpn())); 157 158 mUpdateMonitor.registerInfoCallback(this); 159 //mUpdateMonitor.registerSimStateCallback(this); 160 161 resetStatusInfo(); 162 } 163 164 @Override 165 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 166 // send focus to the password field 167 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); 168 } 169 170 /** {@inheritDoc} */ 171 public boolean needsInput() { 172 return false; 173 } 174 175 /** {@inheritDoc} */ 176 public void onPause() { 177 178 } 179 180 /** {@inheritDoc} */ 181 public void onResume() { 182 // reset status 183 mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils); 184 185 // start fresh 186 mPasswordEntry.setText(""); 187 resetStatusInfo(); 188 mPasswordEntry.requestFocus(); 189 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 190 191 // if the user is currently locked out, enforce it. 192 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); 193 if (deadline != 0) { 194 handleAttemptLockout(deadline); 195 } 196 } 197 198 /** {@inheritDoc} */ 199 public void cleanUp() { 200 mUpdateMonitor.removeCallback(this); 201 } 202 203 public void onClick(View v) { 204 if (v == mEmergencyCallButton) { 205 mCallback.takeEmergencyCallAction(); 206 } 207 mCallback.pokeWakelock(); 208 } 209 210 private void verifyPasswordAndUnlock() { 211 String entry = mPasswordEntry.getText().toString(); 212 if (mLockPatternUtils.checkPassword(entry)) { 213 mCallback.keyguardDone(true); 214 mCallback.reportSuccessfulUnlockAttempt(); 215 mStatusView.setInstructionText(null); 216 } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) { 217 // to avoid accidental lockout, only count attempts that are long enough to be a 218 // real password. This may require some tweaking. 219 mCallback.reportFailedUnlockAttempt(); 220 if (0 == (mUpdateMonitor.getFailedAttempts() 221 % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { 222 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); 223 handleAttemptLockout(deadline); 224 } 225 mStatusView.setInstructionText(R.string.lockscreen_password_wrong); 226 } else if (entry.length() > 0) { 227 mStatusView.setInstructionText(R.string.lockscreen_password_wrong); 228 } 229 mPasswordEntry.setText(""); 230 } 231 232 // Prevent user from using the PIN/Password entry until scheduled deadline. 233 private void handleAttemptLockout(long elapsedRealtimeDeadline) { 234 mPasswordEntry.setEnabled(false); 235 mKeyboardView.setEnabled(false); 236 long elapsedRealtime = SystemClock.elapsedRealtime(); 237 mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { 238 239 @Override 240 public void onTick(long millisUntilFinished) { 241 int secondsRemaining = (int) (millisUntilFinished / 1000); 242 String instructions = getContext().getString( 243 R.string.lockscreen_too_many_failed_attempts_countdown, 244 secondsRemaining); 245 mStatusView.setInstructionText(instructions); 246 } 247 248 @Override 249 public void onFinish() { 250 mPasswordEntry.setEnabled(true); 251 mKeyboardView.setEnabled(true); 252 resetStatusInfo(); 253 } 254 }.start(); 255 } 256 257 258 @Override 259 public boolean onKeyDown(int keyCode, KeyEvent event) { 260 mCallback.pokeWakelock(); 261 return false; 262 } 263 264 @Override 265 protected void onAttachedToWindow() { 266 super.onAttachedToWindow(); 267 Configuration config = getResources().getConfiguration(); 268 if (config.orientation != mCreationOrientation 269 || config.hardKeyboardHidden != mCreationHardKeyboardHidden) { 270 mCallback.recreateMe(config); 271 } 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 protected void onConfigurationChanged(Configuration newConfig) { 277 super.onConfigurationChanged(newConfig); 278 if (newConfig.orientation != mCreationOrientation 279 || newConfig.hardKeyboardHidden != mCreationHardKeyboardHidden) { 280 mCallback.recreateMe(newConfig); 281 } 282 } 283 284 public void onKeyboardChange(boolean isKeyboardOpen) { 285 // Don't show the soft keyboard when the real keyboard is open 286 mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE); 287 } 288 289 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 290 // Check if this was the result of hitting the enter key 291 if (actionId == EditorInfo.IME_NULL) { 292 verifyPasswordAndUnlock(); 293 return true; 294 } 295 return false; 296 } 297 298 // ---------- InfoCallback 299 300 /** {@inheritDoc} */ 301 public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { 302 mStatusView.onRefreshBatteryInfo(showBatteryInfo, pluggedIn, batteryLevel); 303 } 304 305 /** {@inheritDoc} */ 306 public void onTimeChanged() { 307 mStatusView.onTimeChanged(); 308 } 309 310 /** {@inheritDoc} */ 311 public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { 312 mStatusView.onRefreshCarrierInfo(plmn, spn); 313 } 314 315 /** {@inheritDoc} */ 316 public void onRingerModeChanged(int state) { 317 // not currently used 318 } 319 320 // ---------- SimStateCallback 321 322 /** {@inheritDoc} */ 323 public void onPhoneStateChanged(String newState) { 324 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 325 } 326 327 private void resetStatusInfo() { 328 mStatusView.setInstructionText(null); 329 mStatusView.updateStatusLines(true); 330 } 331 332} 333