1/* 2 * Copyright (C) 2008 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.Dialog; 20import android.app.ProgressDialog; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25 26import com.android.internal.telephony.ITelephony; 27import com.android.internal.widget.LockPatternUtils; 28 29import android.text.Editable; 30import android.view.KeyEvent; 31import android.view.LayoutInflater; 32import android.view.View; 33import android.view.WindowManager; 34import android.widget.Button; 35import android.widget.LinearLayout; 36import android.widget.TextView; 37import com.android.internal.R; 38 39/** 40 * Displays a dialer like interface to unlock the SIM PIN. 41 */ 42public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, View.OnClickListener, 43 KeyguardUpdateMonitor.InfoCallback { 44 45 private static final int DIGIT_PRESS_WAKE_MILLIS = 5000; 46 47 private final KeyguardUpdateMonitor mUpdateMonitor; 48 private final KeyguardScreenCallback mCallback; 49 50 private TextView mHeaderText; 51 private TextView mPinText; 52 53 private TextView mOkButton; 54 private Button mEmergencyCallButton; 55 56 private View mBackSpaceButton; 57 58 private final int[] mEnteredPin = {0, 0, 0, 0, 0, 0, 0, 0}; 59 private int mEnteredDigits = 0; 60 61 private ProgressDialog mSimUnlockProgressDialog = null; 62 63 private LockPatternUtils mLockPatternUtils; 64 65 private int mCreationOrientation; 66 67 private int mKeyboardHidden; 68 69 private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; 70 71 public SimUnlockScreen(Context context, Configuration configuration, 72 KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback, 73 LockPatternUtils lockpatternutils) { 74 super(context); 75 mUpdateMonitor = updateMonitor; 76 mCallback = callback; 77 78 mCreationOrientation = configuration.orientation; 79 mKeyboardHidden = configuration.hardKeyboardHidden; 80 mLockPatternUtils = lockpatternutils; 81 82 LayoutInflater inflater = LayoutInflater.from(context); 83 if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { 84 inflater.inflate(R.layout.keyguard_screen_sim_pin_landscape, this, true); 85 } else { 86 inflater.inflate(R.layout.keyguard_screen_sim_pin_portrait, this, true); 87 new TouchInput(); 88 } 89 90 mHeaderText = (TextView) findViewById(R.id.headerText); 91 mPinText = (TextView) findViewById(R.id.pinDisplay); 92 mBackSpaceButton = findViewById(R.id.backspace); 93 mBackSpaceButton.setOnClickListener(this); 94 95 mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall); 96 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 97 mOkButton = (TextView) findViewById(R.id.ok); 98 99 mHeaderText.setText(R.string.keyguard_password_enter_pin_code); 100 mPinText.setFocusable(false); 101 102 mEmergencyCallButton.setOnClickListener(this); 103 mOkButton.setOnClickListener(this); 104 105 setFocusableInTouchMode(true); 106 } 107 108 /** {@inheritDoc} */ 109 public boolean needsInput() { 110 return true; 111 } 112 113 /** {@inheritDoc} */ 114 public void onPause() { 115 116 } 117 118 /** {@inheritDoc} */ 119 public void onResume() { 120 // start fresh 121 mHeaderText.setText(R.string.keyguard_password_enter_pin_code); 122 123 // make sure that the number of entered digits is consistent when we 124 // erase the SIM unlock code, including orientation changes. 125 mPinText.setText(""); 126 mEnteredDigits = 0; 127 128 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 129 } 130 131 /** {@inheritDoc} */ 132 public void cleanUp() { 133 // hide the dialog. 134 if (mSimUnlockProgressDialog != null) { 135 mSimUnlockProgressDialog.hide(); 136 } 137 mUpdateMonitor.removeCallback(this); 138 } 139 140 141 /** 142 * Since the IPC can block, we want to run the request in a separate thread 143 * with a callback. 144 */ 145 private abstract class CheckSimPin extends Thread { 146 147 private final String mPin; 148 149 protected CheckSimPin(String pin) { 150 mPin = pin; 151 } 152 153 abstract void onSimLockChangedResponse(boolean success); 154 155 @Override 156 public void run() { 157 try { 158 final boolean result = ITelephony.Stub.asInterface(ServiceManager 159 .checkService("phone")).supplyPin(mPin); 160 post(new Runnable() { 161 public void run() { 162 onSimLockChangedResponse(result); 163 } 164 }); 165 } catch (RemoteException e) { 166 post(new Runnable() { 167 public void run() { 168 onSimLockChangedResponse(false); 169 } 170 }); 171 } 172 } 173 } 174 175 public void onClick(View v) { 176 if (v == mBackSpaceButton) { 177 final Editable digits = mPinText.getEditableText(); 178 final int len = digits.length(); 179 if (len > 0) { 180 digits.delete(len-1, len); 181 mEnteredDigits--; 182 } 183 mCallback.pokeWakelock(); 184 } else if (v == mEmergencyCallButton) { 185 mCallback.takeEmergencyCallAction(); 186 } else if (v == mOkButton) { 187 checkPin(); 188 } 189 } 190 191 private Dialog getSimUnlockProgressDialog() { 192 if (mSimUnlockProgressDialog == null) { 193 mSimUnlockProgressDialog = new ProgressDialog(mContext); 194 mSimUnlockProgressDialog.setMessage( 195 mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message)); 196 mSimUnlockProgressDialog.setIndeterminate(true); 197 mSimUnlockProgressDialog.setCancelable(false); 198 mSimUnlockProgressDialog.getWindow().setType( 199 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 200 if (!mContext.getResources().getBoolean( 201 com.android.internal.R.bool.config_sf_slowBlur)) { 202 mSimUnlockProgressDialog.getWindow().setFlags( 203 WindowManager.LayoutParams.FLAG_BLUR_BEHIND, 204 WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 205 } 206 } 207 return mSimUnlockProgressDialog; 208 } 209 210 private void checkPin() { 211 212 // make sure that the pin is at least 4 digits long. 213 if (mEnteredDigits < 4) { 214 // otherwise, display a message to the user, and don't submit. 215 mHeaderText.setText(R.string.invalidPin); 216 mPinText.setText(""); 217 mEnteredDigits = 0; 218 mCallback.pokeWakelock(); 219 return; 220 } 221 getSimUnlockProgressDialog().show(); 222 223 new CheckSimPin(mPinText.getText().toString()) { 224 void onSimLockChangedResponse(boolean success) { 225 if (mSimUnlockProgressDialog != null) { 226 mSimUnlockProgressDialog.hide(); 227 } 228 if (success) { 229 // before closing the keyguard, report back that 230 // the sim is unlocked so it knows right away 231 mUpdateMonitor.reportSimPinUnlocked(); 232 mCallback.goToUnlockScreen(); 233 } else { 234 mHeaderText.setText(R.string.keyguard_password_wrong_pin_code); 235 mPinText.setText(""); 236 mEnteredDigits = 0; 237 } 238 mCallback.pokeWakelock(); 239 } 240 }.start(); 241 } 242 243 244 public boolean onKeyDown(int keyCode, KeyEvent event) { 245 if (keyCode == KeyEvent.KEYCODE_BACK) { 246 mCallback.goToLockScreen(); 247 return true; 248 } 249 250 final char match = event.getMatch(DIGITS); 251 if (match != 0) { 252 reportDigit(match - '0'); 253 return true; 254 } 255 if (keyCode == KeyEvent.KEYCODE_DEL) { 256 if (mEnteredDigits > 0) { 257 mPinText.onKeyDown(keyCode, event); 258 mEnteredDigits--; 259 } 260 return true; 261 } 262 263 if (keyCode == KeyEvent.KEYCODE_ENTER) { 264 checkPin(); 265 return true; 266 } 267 268 return false; 269 } 270 271 private void reportDigit(int digit) { 272 if (mEnteredDigits == 0) { 273 mPinText.setText(""); 274 } 275 if (mEnteredDigits == 8) { 276 return; 277 } 278 mPinText.append(Integer.toString(digit)); 279 mEnteredPin[mEnteredDigits++] = digit; 280 } 281 282 void updateConfiguration() { 283 Configuration newConfig = getResources().getConfiguration(); 284 if (newConfig.orientation != mCreationOrientation) { 285 mCallback.recreateMe(newConfig); 286 } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) { 287 mKeyboardHidden = newConfig.hardKeyboardHidden; 288 final boolean isKeyboardOpen = mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; 289 if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) { 290 mCallback.goToUnlockScreen(); 291 } 292 } 293 294 } 295 296 @Override 297 protected void onAttachedToWindow() { 298 super.onAttachedToWindow(); 299 updateConfiguration(); 300 } 301 302 /** {@inheritDoc} */ 303 @Override 304 protected void onConfigurationChanged(Configuration newConfig) { 305 super.onConfigurationChanged(newConfig); 306 updateConfiguration(); 307 } 308 309 /** 310 * Helper class to handle input from touch dialer. Only relevant when 311 * the keyboard is shut. 312 */ 313 private class TouchInput implements View.OnClickListener { 314 private TextView mZero; 315 private TextView mOne; 316 private TextView mTwo; 317 private TextView mThree; 318 private TextView mFour; 319 private TextView mFive; 320 private TextView mSix; 321 private TextView mSeven; 322 private TextView mEight; 323 private TextView mNine; 324 private TextView mCancelButton; 325 326 private TouchInput() { 327 mZero = (TextView) findViewById(R.id.zero); 328 mOne = (TextView) findViewById(R.id.one); 329 mTwo = (TextView) findViewById(R.id.two); 330 mThree = (TextView) findViewById(R.id.three); 331 mFour = (TextView) findViewById(R.id.four); 332 mFive = (TextView) findViewById(R.id.five); 333 mSix = (TextView) findViewById(R.id.six); 334 mSeven = (TextView) findViewById(R.id.seven); 335 mEight = (TextView) findViewById(R.id.eight); 336 mNine = (TextView) findViewById(R.id.nine); 337 mCancelButton = (TextView) findViewById(R.id.cancel); 338 339 mZero.setText("0"); 340 mOne.setText("1"); 341 mTwo.setText("2"); 342 mThree.setText("3"); 343 mFour.setText("4"); 344 mFive.setText("5"); 345 mSix.setText("6"); 346 mSeven.setText("7"); 347 mEight.setText("8"); 348 mNine.setText("9"); 349 350 mZero.setOnClickListener(this); 351 mOne.setOnClickListener(this); 352 mTwo.setOnClickListener(this); 353 mThree.setOnClickListener(this); 354 mFour.setOnClickListener(this); 355 mFive.setOnClickListener(this); 356 mSix.setOnClickListener(this); 357 mSeven.setOnClickListener(this); 358 mEight.setOnClickListener(this); 359 mNine.setOnClickListener(this); 360 mCancelButton.setOnClickListener(this); 361 } 362 363 364 public void onClick(View v) { 365 if (v == mCancelButton) { 366 mCallback.goToLockScreen(); 367 return; 368 } 369 370 final int digit = checkDigit(v); 371 if (digit >= 0) { 372 mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS); 373 reportDigit(digit); 374 } 375 } 376 377 private int checkDigit(View v) { 378 int digit = -1; 379 if (v == mZero) { 380 digit = 0; 381 } else if (v == mOne) { 382 digit = 1; 383 } else if (v == mTwo) { 384 digit = 2; 385 } else if (v == mThree) { 386 digit = 3; 387 } else if (v == mFour) { 388 digit = 4; 389 } else if (v == mFive) { 390 digit = 5; 391 } else if (v == mSix) { 392 digit = 6; 393 } else if (v == mSeven) { 394 digit = 7; 395 } else if (v == mEight) { 396 digit = 8; 397 } else if (v == mNine) { 398 digit = 9; 399 } 400 return digit; 401 } 402 } 403 404 public void onPhoneStateChanged(String newState) { 405 mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton); 406 } 407 408 public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { 409 410 } 411 412 public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { 413 414 } 415 416 public void onRingerModeChanged(int state) { 417 418 } 419 420 public void onTimeChanged() { 421 422 } 423} 424