/* * Copyright (C) 2006 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.phone; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.content.res.Resources; import android.os.AsyncResult; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.method.DigitsKeyListener; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.Phone; /** * "Change ICC PIN" UI for the Phone app. */ public class ChangeIccPinScreen extends Activity { private static final String LOG_TAG = PhoneApp.LOG_TAG; private static final boolean DBG = false; private static final int EVENT_PIN_CHANGED = 100; private enum EntryState { ES_PIN, ES_PUK } private EntryState mState; private static final int NO_ERROR = 0; private static final int PIN_MISMATCH = 1; private static final int PIN_INVALID_LENGTH = 2; private static final int MIN_PIN_LENGTH = 4; private static final int MAX_PIN_LENGTH = 8; private Phone mPhone; private boolean mChangePin2; private TextView mBadPinError; private TextView mMismatchError; private EditText mOldPin; private EditText mNewPin1; private EditText mNewPin2; private EditText mPUKCode; private Button mButton; private Button mPUKSubmit; private ScrollView mScrollView; private LinearLayout mIccPUKPanel; private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case EVENT_PIN_CHANGED: AsyncResult ar = (AsyncResult) msg.obj; handleResult(ar); break; } return; } }; public void onCreate(Bundle icicle) { super.onCreate(icicle); mPhone = PhoneApp.getPhone(); resolveIntent(); setContentView(R.layout.change_sim_pin_screen); mOldPin = (EditText) findViewById(R.id.old_pin); mOldPin.setKeyListener(DigitsKeyListener.getInstance()); mOldPin.setMovementMethod(null); mOldPin.setOnClickListener(mClicked); mNewPin1 = (EditText) findViewById(R.id.new_pin1); mNewPin1.setKeyListener(DigitsKeyListener.getInstance()); mNewPin1.setMovementMethod(null); mNewPin1.setOnClickListener(mClicked); mNewPin2 = (EditText) findViewById(R.id.new_pin2); mNewPin2.setKeyListener(DigitsKeyListener.getInstance()); mNewPin2.setMovementMethod(null); mNewPin2.setOnClickListener(mClicked); mBadPinError = (TextView) findViewById(R.id.bad_pin); mMismatchError = (TextView) findViewById(R.id.mismatch); mButton = (Button) findViewById(R.id.button); mButton.setOnClickListener(mClicked); mScrollView = (ScrollView) findViewById(R.id.scroll); mPUKCode = (EditText) findViewById(R.id.puk_code); mPUKCode.setKeyListener(DigitsKeyListener.getInstance()); mPUKCode.setMovementMethod(null); mPUKCode.setOnClickListener(mClicked); mPUKSubmit = (Button) findViewById(R.id.puk_submit); mPUKSubmit.setOnClickListener(mClicked); mIccPUKPanel = (LinearLayout) findViewById(R.id.puk_panel); int id = mChangePin2 ? R.string.change_pin2 : R.string.change_pin; setTitle(getResources().getText(id)); mState = EntryState.ES_PIN; } private void resolveIntent() { Intent intent = getIntent(); mChangePin2 = intent.getBooleanExtra("pin2", mChangePin2); } private void reset() { mScrollView.scrollTo(0, 0); mBadPinError.setVisibility(View.GONE); mMismatchError.setVisibility(View.GONE); } private int validateNewPin(String p1, String p2) { if (p1 == null) { return PIN_INVALID_LENGTH; } if (!p1.equals(p2)) { return PIN_MISMATCH; } int len1 = p1.length(); if (len1 < MIN_PIN_LENGTH || len1 > MAX_PIN_LENGTH) { return PIN_INVALID_LENGTH; } return NO_ERROR; } private View.OnClickListener mClicked = new View.OnClickListener() { public void onClick(View v) { if (v == mOldPin) { mNewPin1.requestFocus(); } else if (v == mNewPin1) { mNewPin2.requestFocus(); } else if (v == mNewPin2) { mButton.requestFocus(); } else if (v == mButton) { IccCard iccCardInterface = mPhone.getIccCard(); if (iccCardInterface != null) { String oldPin = mOldPin.getText().toString(); String newPin1 = mNewPin1.getText().toString(); String newPin2 = mNewPin2.getText().toString(); int error = validateNewPin(newPin1, newPin2); switch (error) { case PIN_INVALID_LENGTH: case PIN_MISMATCH: mNewPin1.getText().clear(); mNewPin2.getText().clear(); mMismatchError.setVisibility(View.VISIBLE); Resources r = getResources(); CharSequence text; if (error == PIN_MISMATCH) { text = r.getString(R.string.mismatchPin); } else { text = r.getString(R.string.invalidPin); } mMismatchError.setText(text); break; default: Message callBack = Message.obtain(mHandler, EVENT_PIN_CHANGED); if (DBG) log("change pin attempt: old=" + oldPin + ", newPin=" + newPin1); reset(); if (mChangePin2) { iccCardInterface.changeIccFdnPassword(oldPin, newPin1, callBack); } else { iccCardInterface.changeIccLockPassword(oldPin, newPin1, callBack); } // TODO: show progress panel } } } else if (v == mPUKCode) { mPUKSubmit.requestFocus(); } else if (v == mPUKSubmit) { mPhone.getIccCard().supplyPuk2(mPUKCode.getText().toString(), mNewPin1.getText().toString(), Message.obtain(mHandler, EVENT_PIN_CHANGED)); } } }; private void handleResult(AsyncResult ar) { if (ar.exception == null) { if (DBG) log("handleResult: success!"); if (mState == EntryState.ES_PUK) { mScrollView.setVisibility(View.VISIBLE); mIccPUKPanel.setVisibility(View.GONE); } // TODO: show success feedback showConfirmation(); mHandler.postDelayed(new Runnable() { public void run() { finish(); } }, 3000); } else if (ar.exception instanceof CommandException /* && ((CommandException)ar.exception).getCommandError() == CommandException.Error.PASSWORD_INCORRECT */ ) { if (mState == EntryState.ES_PIN) { if (DBG) log("handleResult: pin failed!"); mOldPin.getText().clear(); mBadPinError.setVisibility(View.VISIBLE); CommandException ce = (CommandException) ar.exception; if (ce.getCommandError() == CommandException.Error.SIM_PUK2) { if (DBG) log("handleResult: puk requested!"); mState = EntryState.ES_PUK; displayPUKAlert(); mScrollView.setVisibility(View.GONE); mIccPUKPanel.setVisibility(View.VISIBLE); mPUKCode.requestFocus(); } } else if (mState == EntryState.ES_PUK) { //should really check to see if the error is CommandException.PASSWORD_INCORRECT... if (DBG) log("handleResult: puk2 failed!"); displayPUKAlert(); mPUKCode.getText().clear(); mPUKCode.requestFocus(); } } } private AlertDialog mPUKAlert; private void displayPUKAlert () { if (mPUKAlert == null) { mPUKAlert = new AlertDialog.Builder(this) .setMessage (R.string.puk_requested) .setCancelable(false) .show(); } else { mPUKAlert.show(); } //TODO: The 3 second delay here is somewhat arbitrary, reflecting the values //used elsewhere for similar code. This should get revisited with the framework //crew to see if there is some standard we should adhere to. mHandler.postDelayed(new Runnable() { public void run() { mPUKAlert.dismiss(); } }, 3000); } private void showConfirmation() { int id = mChangePin2 ? R.string.pin2_changed : R.string.pin_changed; Toast.makeText(this, id, Toast.LENGTH_SHORT).show(); } private void log(String msg) { String prefix = mChangePin2 ? "[ChgPin2]" : "[ChgPin]"; Log.d(LOG_TAG, prefix + msg); } }