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.view.KeyEvent; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.inputmethod.EditorInfo; 37import android.widget.Button; 38import android.widget.EditText; 39import android.widget.LinearLayout; 40import android.widget.TextView; 41import android.widget.TextView.OnEditorActionListener; 42 43import com.android.internal.R; 44import com.android.internal.widget.PasswordEntryKeyboardHelper; 45 46/** 47 * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter 48 * an unlock password 49 */ 50public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen, 51 View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener { 52 53 private final KeyguardUpdateMonitor mUpdateMonitor; 54 private final KeyguardScreenCallback mCallback; 55 56 private EditText mPasswordEntry; 57 private Button mEmergencyCallButton; 58 private LockPatternUtils mLockPatternUtils; 59 private PasswordEntryKeyboardView mKeyboardView; 60 private PasswordEntryKeyboardHelper mKeyboardHelper; 61 62 private int mCreationOrientation; 63 private int mCreationHardKeyboardHidden; 64 private CountDownTimer mCountdownTimer; 65 private TextView mTitle; 66 67 // To avoid accidental lockout due to events while the device in in the pocket, ignore 68 // any passwords with length less than or equal to this length. 69 private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; 70 71 public PasswordUnlockScreen(Context context, Configuration configuration, 72 LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor, 73 KeyguardScreenCallback callback) { 74 super(context); 75 76 mCreationHardKeyboardHidden = configuration.hardKeyboardHidden; 77 mCreationOrientation = configuration.orientation; 78 mUpdateMonitor = updateMonitor; 79 mCallback = callback; 80 mLockPatternUtils = lockPatternUtils; 81 82 LayoutInflater layoutInflater = LayoutInflater.from(context); 83 if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) { 84 layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true); 85 } else { 86 layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true); 87 } 88 89 final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality(); 90 final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality 91 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality; 92 93 mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); 94 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); 95 mPasswordEntry.setOnEditorActionListener(this); 96 mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall); 97 mEmergencyCallButton.setOnClickListener(this); 98 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 99 mTitle = (TextView) findViewById(R.id.enter_password_label); 100 101 mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this); 102 mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA 103 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); 104 105 mKeyboardView.setVisibility(mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO 106 ? View.INVISIBLE : View.VISIBLE); 107 mPasswordEntry.requestFocus(); 108 109 // This allows keyboards with overlapping qwerty/numeric keys to choose just the 110 // numeric keys. 111 if (isAlpha) { 112 mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); 113 } else { 114 mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); 115 } 116 117 mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ? 118 com.android.internal.R.array.config_virtualKeyVibePattern : 0); 119 } 120 121 @Override 122 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 123 // send focus to the password field 124 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); 125 } 126 127 /** {@inheritDoc} */ 128 public boolean needsInput() { 129 return false; 130 } 131 132 /** {@inheritDoc} */ 133 public void onPause() { 134 135 } 136 137 /** {@inheritDoc} */ 138 public void onResume() { 139 // start fresh 140 mPasswordEntry.setText(""); 141 mPasswordEntry.requestFocus(); 142 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 143 144 // if the user is currently locked out, enforce it. 145 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); 146 if (deadline != 0) { 147 handleAttemptLockout(deadline); 148 } 149 } 150 151 /** {@inheritDoc} */ 152 public void cleanUp() { 153 mUpdateMonitor.removeCallback(this); 154 } 155 156 public void onClick(View v) { 157 if (v == mEmergencyCallButton) { 158 mCallback.takeEmergencyCallAction(); 159 } 160 mCallback.pokeWakelock(); 161 } 162 163 private void verifyPasswordAndUnlock() { 164 String entry = mPasswordEntry.getText().toString(); 165 if (mLockPatternUtils.checkPassword(entry)) { 166 mCallback.keyguardDone(true); 167 mCallback.reportSuccessfulUnlockAttempt(); 168 } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) { 169 // to avoid accidental lockout, only count attempts that are long enough to be a 170 // real password. This may require some tweaking. 171 mCallback.reportFailedUnlockAttempt(); 172 if (0 == (mUpdateMonitor.getFailedAttempts() 173 % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { 174 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); 175 handleAttemptLockout(deadline); 176 } 177 } 178 mPasswordEntry.setText(""); 179 } 180 181 // Prevent user from using the PIN/Password entry until scheduled deadline. 182 private void handleAttemptLockout(long elapsedRealtimeDeadline) { 183 mPasswordEntry.setEnabled(false); 184 mKeyboardView.setEnabled(false); 185 long elapsedRealtime = SystemClock.elapsedRealtime(); 186 mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { 187 188 @Override 189 public void onTick(long millisUntilFinished) { 190 int secondsRemaining = (int) (millisUntilFinished / 1000); 191 String instructions = getContext().getString( 192 R.string.lockscreen_too_many_failed_attempts_countdown, 193 secondsRemaining); 194 mTitle.setText(instructions); 195 } 196 197 @Override 198 public void onFinish() { 199 mPasswordEntry.setEnabled(true); 200 mTitle.setText(R.string.keyguard_password_enter_password_code); 201 mKeyboardView.setEnabled(true); 202 } 203 }.start(); 204 } 205 206 207 @Override 208 public boolean onKeyDown(int keyCode, KeyEvent event) { 209 mCallback.pokeWakelock(); 210 return false; 211 } 212 213 @Override 214 protected void onAttachedToWindow() { 215 super.onAttachedToWindow(); 216 Configuration config = getResources().getConfiguration(); 217 if (config.orientation != mCreationOrientation 218 || config.hardKeyboardHidden != mCreationHardKeyboardHidden) { 219 mCallback.recreateMe(config); 220 } 221 } 222 223 /** {@inheritDoc} */ 224 @Override 225 protected void onConfigurationChanged(Configuration newConfig) { 226 super.onConfigurationChanged(newConfig); 227 if (newConfig.orientation != mCreationOrientation 228 || newConfig.hardKeyboardHidden != mCreationHardKeyboardHidden) { 229 mCallback.recreateMe(newConfig); 230 } 231 } 232 233 public void onKeyboardChange(boolean isKeyboardOpen) { 234 // Don't show the soft keyboard when the real keyboard is open 235 mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE); 236 } 237 238 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 239 // Check if this was the result of hitting the enter key 240 if (actionId == EditorInfo.IME_NULL) { 241 verifyPasswordAndUnlock(); 242 return true; 243 } 244 return false; 245 } 246 247 public void onPhoneStateChanged(String newState) { 248 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 249 } 250 251 public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { 252 253 } 254 255 public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { 256 257 } 258 259 public void onRingerModeChanged(int state) { 260 261 } 262 263 public void onTimeChanged() { 264 265 } 266 267} 268