1/* 2 * Copyright (C) 2006 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.phone; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.content.Intent; 22import android.content.res.Resources; 23import android.os.AsyncResult; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.Message; 27import android.text.method.DigitsKeyListener; 28import android.util.Log; 29import android.view.View; 30import android.widget.Button; 31import android.widget.EditText; 32import android.widget.LinearLayout; 33import android.widget.ScrollView; 34import android.widget.TextView; 35import android.widget.Toast; 36 37import com.android.internal.telephony.CommandException; 38import com.android.internal.telephony.IccCard; 39import com.android.internal.telephony.Phone; 40 41/** 42 * "Change ICC PIN" UI for the Phone app. 43 */ 44public class ChangeIccPinScreen extends Activity { 45 private static final String LOG_TAG = PhoneGlobals.LOG_TAG; 46 private static final boolean DBG = false; 47 48 private static final int EVENT_PIN_CHANGED = 100; 49 50 private enum EntryState { 51 ES_PIN, 52 ES_PUK 53 } 54 55 private EntryState mState; 56 57 private static final int NO_ERROR = 0; 58 private static final int PIN_MISMATCH = 1; 59 private static final int PIN_INVALID_LENGTH = 2; 60 61 private static final int MIN_PIN_LENGTH = 4; 62 private static final int MAX_PIN_LENGTH = 8; 63 64 private Phone mPhone; 65 private boolean mChangePin2; 66 private TextView mBadPinError; 67 private TextView mMismatchError; 68 private EditText mOldPin; 69 private EditText mNewPin1; 70 private EditText mNewPin2; 71 private EditText mPUKCode; 72 private Button mButton; 73 private Button mPUKSubmit; 74 private ScrollView mScrollView; 75 76 private LinearLayout mIccPUKPanel; 77 78 private Handler mHandler = new Handler() { 79 public void handleMessage(Message msg) { 80 switch (msg.what) { 81 case EVENT_PIN_CHANGED: 82 AsyncResult ar = (AsyncResult) msg.obj; 83 handleResult(ar); 84 break; 85 } 86 87 return; 88 } 89 }; 90 91 public void onCreate(Bundle icicle) { 92 super.onCreate(icicle); 93 94 mPhone = PhoneGlobals.getPhone(); 95 96 resolveIntent(); 97 98 setContentView(R.layout.change_sim_pin_screen); 99 100 mOldPin = (EditText) findViewById(R.id.old_pin); 101 mOldPin.setKeyListener(DigitsKeyListener.getInstance()); 102 mOldPin.setMovementMethod(null); 103 mOldPin.setOnClickListener(mClicked); 104 105 mNewPin1 = (EditText) findViewById(R.id.new_pin1); 106 mNewPin1.setKeyListener(DigitsKeyListener.getInstance()); 107 mNewPin1.setMovementMethod(null); 108 mNewPin1.setOnClickListener(mClicked); 109 110 mNewPin2 = (EditText) findViewById(R.id.new_pin2); 111 mNewPin2.setKeyListener(DigitsKeyListener.getInstance()); 112 mNewPin2.setMovementMethod(null); 113 mNewPin2.setOnClickListener(mClicked); 114 115 mBadPinError = (TextView) findViewById(R.id.bad_pin); 116 mMismatchError = (TextView) findViewById(R.id.mismatch); 117 118 mButton = (Button) findViewById(R.id.button); 119 mButton.setOnClickListener(mClicked); 120 121 mScrollView = (ScrollView) findViewById(R.id.scroll); 122 123 mPUKCode = (EditText) findViewById(R.id.puk_code); 124 mPUKCode.setKeyListener(DigitsKeyListener.getInstance()); 125 mPUKCode.setMovementMethod(null); 126 mPUKCode.setOnClickListener(mClicked); 127 128 mPUKSubmit = (Button) findViewById(R.id.puk_submit); 129 mPUKSubmit.setOnClickListener(mClicked); 130 131 mIccPUKPanel = (LinearLayout) findViewById(R.id.puk_panel); 132 133 int id = mChangePin2 ? R.string.change_pin2 : R.string.change_pin; 134 setTitle(getResources().getText(id)); 135 136 mState = EntryState.ES_PIN; 137 } 138 139 private void resolveIntent() { 140 Intent intent = getIntent(); 141 mChangePin2 = intent.getBooleanExtra("pin2", mChangePin2); 142 } 143 144 private void reset() { 145 mScrollView.scrollTo(0, 0); 146 mBadPinError.setVisibility(View.GONE); 147 mMismatchError.setVisibility(View.GONE); 148 } 149 150 private int validateNewPin(String p1, String p2) { 151 if (p1 == null) { 152 return PIN_INVALID_LENGTH; 153 } 154 155 if (!p1.equals(p2)) { 156 return PIN_MISMATCH; 157 } 158 159 int len1 = p1.length(); 160 161 if (len1 < MIN_PIN_LENGTH || len1 > MAX_PIN_LENGTH) { 162 return PIN_INVALID_LENGTH; 163 } 164 165 return NO_ERROR; 166 } 167 168 private View.OnClickListener mClicked = new View.OnClickListener() { 169 public void onClick(View v) { 170 if (v == mOldPin) { 171 mNewPin1.requestFocus(); 172 } else if (v == mNewPin1) { 173 mNewPin2.requestFocus(); 174 } else if (v == mNewPin2) { 175 mButton.requestFocus(); 176 } else if (v == mButton) { 177 IccCard iccCardInterface = mPhone.getIccCard(); 178 if (iccCardInterface != null) { 179 String oldPin = mOldPin.getText().toString(); 180 String newPin1 = mNewPin1.getText().toString(); 181 String newPin2 = mNewPin2.getText().toString(); 182 183 int error = validateNewPin(newPin1, newPin2); 184 185 switch (error) { 186 case PIN_INVALID_LENGTH: 187 case PIN_MISMATCH: 188 mNewPin1.getText().clear(); 189 mNewPin2.getText().clear(); 190 mMismatchError.setVisibility(View.VISIBLE); 191 192 Resources r = getResources(); 193 CharSequence text; 194 195 if (error == PIN_MISMATCH) { 196 text = r.getString(R.string.mismatchPin); 197 } else { 198 text = r.getString(R.string.invalidPin); 199 } 200 201 mMismatchError.setText(text); 202 break; 203 204 default: 205 Message callBack = Message.obtain(mHandler, 206 EVENT_PIN_CHANGED); 207 208 if (DBG) log("change pin attempt: old=" + oldPin + 209 ", newPin=" + newPin1); 210 211 reset(); 212 213 if (mChangePin2) { 214 iccCardInterface.changeIccFdnPassword(oldPin, 215 newPin1, callBack); 216 } else { 217 iccCardInterface.changeIccLockPassword(oldPin, 218 newPin1, callBack); 219 } 220 221 // TODO: show progress panel 222 } 223 } 224 } else if (v == mPUKCode) { 225 mPUKSubmit.requestFocus(); 226 } else if (v == mPUKSubmit) { 227 mPhone.getIccCard().supplyPuk2(mPUKCode.getText().toString(), 228 mNewPin1.getText().toString(), 229 Message.obtain(mHandler, EVENT_PIN_CHANGED)); 230 } 231 } 232 }; 233 234 private void handleResult(AsyncResult ar) { 235 if (ar.exception == null) { 236 if (DBG) log("handleResult: success!"); 237 238 if (mState == EntryState.ES_PUK) { 239 mScrollView.setVisibility(View.VISIBLE); 240 mIccPUKPanel.setVisibility(View.GONE); 241 } 242 // TODO: show success feedback 243 showConfirmation(); 244 245 mHandler.postDelayed(new Runnable() { 246 public void run() { 247 finish(); 248 } 249 }, 3000); 250 251 } else if (ar.exception instanceof CommandException 252 /* && ((CommandException)ar.exception).getCommandError() == 253 CommandException.Error.PASSWORD_INCORRECT */ ) { 254 if (mState == EntryState.ES_PIN) { 255 if (DBG) log("handleResult: pin failed!"); 256 mOldPin.getText().clear(); 257 mBadPinError.setVisibility(View.VISIBLE); 258 CommandException ce = (CommandException) ar.exception; 259 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) { 260 if (DBG) log("handleResult: puk requested!"); 261 mState = EntryState.ES_PUK; 262 displayPUKAlert(); 263 mScrollView.setVisibility(View.GONE); 264 mIccPUKPanel.setVisibility(View.VISIBLE); 265 mPUKCode.requestFocus(); 266 } 267 } else if (mState == EntryState.ES_PUK) { 268 //should really check to see if the error is CommandException.PASSWORD_INCORRECT... 269 if (DBG) log("handleResult: puk2 failed!"); 270 displayPUKAlert(); 271 mPUKCode.getText().clear(); 272 mPUKCode.requestFocus(); 273 } 274 } 275 } 276 277 private AlertDialog mPUKAlert; 278 private void displayPUKAlert () { 279 if (mPUKAlert == null) { 280 mPUKAlert = new AlertDialog.Builder(this) 281 .setMessage (R.string.puk_requested) 282 .setCancelable(false) 283 .show(); 284 } else { 285 mPUKAlert.show(); 286 } 287 //TODO: The 3 second delay here is somewhat arbitrary, reflecting the values 288 //used elsewhere for similar code. This should get revisited with the framework 289 //crew to see if there is some standard we should adhere to. 290 mHandler.postDelayed(new Runnable() { 291 public void run() { 292 mPUKAlert.dismiss(); 293 } 294 }, 3000); 295 } 296 297 private void showConfirmation() { 298 int id = mChangePin2 ? R.string.pin2_changed : R.string.pin_changed; 299 Toast.makeText(this, id, Toast.LENGTH_SHORT).show(); 300 } 301 302 private void log(String msg) { 303 String prefix = mChangePin2 ? "[ChgPin2]" : "[ChgPin]"; 304 Log.d(LOG_TAG, prefix + msg); 305 } 306} 307