/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.policy.impl; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.res.Configuration; import android.os.RemoteException; import android.os.ServiceManager; import com.android.internal.telephony.ITelephony; import com.android.internal.widget.LockPatternUtils; import android.text.Editable; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.R; /** * Displays a dialer like interface to unlock the SIM PIN. */ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, View.OnClickListener { private static final int DIGIT_PRESS_WAKE_MILLIS = 5000; private final KeyguardUpdateMonitor mUpdateMonitor; private final KeyguardScreenCallback mCallback; private TextView mHeaderText; private TextView mPinText; private TextView mOkButton; private View mBackSpaceButton; private final int[] mEnteredPin = {0, 0, 0, 0, 0, 0, 0, 0}; private int mEnteredDigits = 0; private ProgressDialog mSimUnlockProgressDialog = null; private LockPatternUtils mLockPatternUtils; private int mCreationOrientation; private int mKeyboardHidden; private KeyguardStatusViewManager mKeyguardStatusViewManager; private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; public SimUnlockScreen(Context context, Configuration configuration, KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback, LockPatternUtils lockpatternutils) { super(context); mUpdateMonitor = updateMonitor; mCallback = callback; mCreationOrientation = configuration.orientation; mKeyboardHidden = configuration.hardKeyboardHidden; mLockPatternUtils = lockpatternutils; LayoutInflater inflater = LayoutInflater.from(context); if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { inflater.inflate(R.layout.keyguard_screen_sim_pin_landscape, this, true); } else { inflater.inflate(R.layout.keyguard_screen_sim_pin_portrait, this, true); new TouchInput(); } mHeaderText = (TextView) findViewById(R.id.headerText); mPinText = (TextView) findViewById(R.id.pinDisplay); mBackSpaceButton = findViewById(R.id.backspace); mBackSpaceButton.setOnClickListener(this); mOkButton = (TextView) findViewById(R.id.ok); mHeaderText.setText(R.string.keyguard_password_enter_pin_code); mPinText.setFocusable(false); mOkButton.setOnClickListener(this); mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor, lockpatternutils, callback, false); setFocusableInTouchMode(true); } /** {@inheritDoc} */ public boolean needsInput() { return true; } /** {@inheritDoc} */ public void onPause() { mKeyguardStatusViewManager.onPause(); } /** {@inheritDoc} */ public void onResume() { // start fresh mHeaderText.setText(R.string.keyguard_password_enter_pin_code); // make sure that the number of entered digits is consistent when we // erase the SIM unlock code, including orientation changes. mPinText.setText(""); mEnteredDigits = 0; mKeyguardStatusViewManager.onResume(); } /** {@inheritDoc} */ public void cleanUp() { // dismiss the dialog. if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.dismiss(); mSimUnlockProgressDialog = null; } mUpdateMonitor.removeCallback(this); } /** * Since the IPC can block, we want to run the request in a separate thread * with a callback. */ private abstract class CheckSimPin extends Thread { private final String mPin; protected CheckSimPin(String pin) { mPin = pin; } abstract void onSimLockChangedResponse(boolean success); @Override public void run() { try { final boolean result = ITelephony.Stub.asInterface(ServiceManager .checkService("phone")).supplyPin(mPin); post(new Runnable() { public void run() { onSimLockChangedResponse(result); } }); } catch (RemoteException e) { post(new Runnable() { public void run() { onSimLockChangedResponse(false); } }); } } } public void onClick(View v) { if (v == mBackSpaceButton) { final Editable digits = mPinText.getEditableText(); final int len = digits.length(); if (len > 0) { digits.delete(len-1, len); mEnteredDigits--; } mCallback.pokeWakelock(); } else if (v == mOkButton) { checkPin(); } } private Dialog getSimUnlockProgressDialog() { if (mSimUnlockProgressDialog == null) { mSimUnlockProgressDialog = new ProgressDialog(mContext); mSimUnlockProgressDialog.setMessage( mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message)); mSimUnlockProgressDialog.setIndeterminate(true); mSimUnlockProgressDialog.setCancelable(false); mSimUnlockProgressDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); } return mSimUnlockProgressDialog; } private void checkPin() { // make sure that the pin is at least 4 digits long. if (mEnteredDigits < 4) { // otherwise, display a message to the user, and don't submit. mHeaderText.setText(R.string.invalidPin); mPinText.setText(""); mEnteredDigits = 0; mCallback.pokeWakelock(); return; } getSimUnlockProgressDialog().show(); new CheckSimPin(mPinText.getText().toString()) { void onSimLockChangedResponse(final boolean success) { mPinText.post(new Runnable() { public void run() { if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } if (success) { // before closing the keyguard, report back that // the sim is unlocked so it knows right away mUpdateMonitor.reportSimUnlocked(); mCallback.goToUnlockScreen(); } else { mHeaderText.setText(R.string.keyguard_password_wrong_pin_code); mPinText.setText(""); mEnteredDigits = 0; } mCallback.pokeWakelock(); } }); } }.start(); } public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { mCallback.goToLockScreen(); return true; } final char match = event.getMatch(DIGITS); if (match != 0) { reportDigit(match - '0'); return true; } if (keyCode == KeyEvent.KEYCODE_DEL) { if (mEnteredDigits > 0) { mPinText.onKeyDown(keyCode, event); mEnteredDigits--; } return true; } if (keyCode == KeyEvent.KEYCODE_ENTER) { checkPin(); return true; } return false; } private void reportDigit(int digit) { if (mEnteredDigits == 0) { mPinText.setText(""); } if (mEnteredDigits == 8) { return; } mPinText.append(Integer.toString(digit)); mEnteredPin[mEnteredDigits++] = digit; } void updateConfiguration() { Configuration newConfig = getResources().getConfiguration(); if (newConfig.orientation != mCreationOrientation) { mCallback.recreateMe(newConfig); } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) { mKeyboardHidden = newConfig.hardKeyboardHidden; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateConfiguration(); } /** {@inheritDoc} */ @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); updateConfiguration(); } /** * Helper class to handle input from touch dialer. Only relevant when * the keyboard is shut. */ private class TouchInput implements View.OnClickListener { private TextView mZero; private TextView mOne; private TextView mTwo; private TextView mThree; private TextView mFour; private TextView mFive; private TextView mSix; private TextView mSeven; private TextView mEight; private TextView mNine; private TextView mCancelButton; private TouchInput() { mZero = (TextView) findViewById(R.id.zero); mOne = (TextView) findViewById(R.id.one); mTwo = (TextView) findViewById(R.id.two); mThree = (TextView) findViewById(R.id.three); mFour = (TextView) findViewById(R.id.four); mFive = (TextView) findViewById(R.id.five); mSix = (TextView) findViewById(R.id.six); mSeven = (TextView) findViewById(R.id.seven); mEight = (TextView) findViewById(R.id.eight); mNine = (TextView) findViewById(R.id.nine); mCancelButton = (TextView) findViewById(R.id.cancel); mZero.setText("0"); mOne.setText("1"); mTwo.setText("2"); mThree.setText("3"); mFour.setText("4"); mFive.setText("5"); mSix.setText("6"); mSeven.setText("7"); mEight.setText("8"); mNine.setText("9"); mZero.setOnClickListener(this); mOne.setOnClickListener(this); mTwo.setOnClickListener(this); mThree.setOnClickListener(this); mFour.setOnClickListener(this); mFive.setOnClickListener(this); mSix.setOnClickListener(this); mSeven.setOnClickListener(this); mEight.setOnClickListener(this); mNine.setOnClickListener(this); mCancelButton.setOnClickListener(this); } public void onClick(View v) { if (v == mCancelButton) { mPinText.setText(""); // clear the PIN entry field if the user cancels mCallback.goToLockScreen(); return; } final int digit = checkDigit(v); if (digit >= 0) { mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS); reportDigit(digit); } } private int checkDigit(View v) { int digit = -1; if (v == mZero) { digit = 0; } else if (v == mOne) { digit = 1; } else if (v == mTwo) { digit = 2; } else if (v == mThree) { digit = 3; } else if (v == mFour) { digit = 4; } else if (v == mFive) { digit = 5; } else if (v == mSix) { digit = 6; } else if (v == mSeven) { digit = 7; } else if (v == mEight) { digit = 8; } else if (v == mNine) { digit = 9; } return digit; } } }