blob: a471093f16917dd385b5bdd8df1caf0e69158af5 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
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.phone;
18
19import android.app.ActionBar;
20import android.app.AlertDialog;
21import android.content.DialogInterface;
22import android.os.AsyncResult;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Message;
26import android.util.Log;
27import android.preference.PreferenceActivity;
28import android.preference.PreferenceScreen;
29import android.view.MenuItem;
30import android.view.WindowManager;
31import android.widget.Toast;
32
33import com.android.internal.telephony.CommandException;
34import com.android.internal.telephony.Phone;
35
36/**
37 * FDN settings UI for the Phone app.
38 * Rewritten to look and behave closer to the other preferences.
39 */
40public class FdnSetting extends PreferenceActivity
41 implements EditPinPreference.OnPinEnteredListener, DialogInterface.OnCancelListener {
42
43 private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
44 private static final boolean DBG = false;
45
46 private Phone mPhone;
47
48 /**
49 * Events we handle.
50 * The first is used for toggling FDN enable, the second for the PIN change.
51 */
52 private static final int EVENT_PIN2_ENTRY_COMPLETE = 100;
53 private static final int EVENT_PIN2_CHANGE_COMPLETE = 200;
54
55 // String keys for preference lookup
56 // We only care about the pin preferences here, the manage FDN contacts
57 // Preference is handled solely in xml.
58 private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key";
59 private static final String BUTTON_CHANGE_PIN2_KEY = "button_change_pin2_key";
60
61 private EditPinPreference mButtonEnableFDN;
62 private EditPinPreference mButtonChangePin2;
63
64 // State variables
65 private String mOldPin;
66 private String mNewPin;
67 private String mPuk2;
68 private static final int PIN_CHANGE_OLD = 0;
69 private static final int PIN_CHANGE_NEW = 1;
70 private static final int PIN_CHANGE_REENTER = 2;
71 private static final int PIN_CHANGE_PUK = 3;
72 private static final int PIN_CHANGE_NEW_PIN_FOR_PUK = 4;
73 private static final int PIN_CHANGE_REENTER_PIN_FOR_PUK = 5;
74 private int mPinChangeState;
75 private boolean mIsPuk2Locked; // Indicates we know that we are PUK2 blocked.
76
77 private static final String SKIP_OLD_PIN_KEY = "skip_old_pin_key";
78 private static final String PIN_CHANGE_STATE_KEY = "pin_change_state_key";
79 private static final String OLD_PIN_KEY = "old_pin_key";
80 private static final String NEW_PIN_KEY = "new_pin_key";
81 private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
82 private static final String DIALOG_PIN_ENTRY_KEY = "dialog_pin_entry_key";
83
84 // size limits for the pin.
85 private static final int MIN_PIN_LENGTH = 4;
86 private static final int MAX_PIN_LENGTH = 8;
87
88 /**
89 * Delegate to the respective handlers.
90 */
91 @Override
92 public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
93 if (preference == mButtonEnableFDN) {
94 toggleFDNEnable(positiveResult);
95 } else if (preference == mButtonChangePin2){
96 updatePINChangeState(positiveResult);
97 }
98 }
99
100 /**
101 * Attempt to toggle FDN activation.
102 */
103 private void toggleFDNEnable(boolean positiveResult) {
104 if (!positiveResult) {
105 return;
106 }
107
108 // validate the pin first, before submitting it to the RIL for FDN enable.
109 String password = mButtonEnableFDN.getText();
110 if (validatePin (password, false)) {
111 // get the relevant data for the icc call
112 boolean isEnabled = mPhone.getIccCard().getIccFdnEnabled();
113 Message onComplete = mFDNHandler.obtainMessage(EVENT_PIN2_ENTRY_COMPLETE);
114
115 // make fdn request
116 mPhone.getIccCard().setIccFdnEnabled(!isEnabled, password, onComplete);
117 } else {
118 // throw up error if the pin is invalid.
119 displayMessage(R.string.invalidPin2);
120 }
121
122 mButtonEnableFDN.setText("");
123 }
124
125 /**
126 * Attempt to change the pin.
127 */
128 private void updatePINChangeState(boolean positiveResult) {
129 if (DBG) log("updatePINChangeState positive=" + positiveResult
130 + " mPinChangeState=" + mPinChangeState
131 + " mSkipOldPin=" + mIsPuk2Locked);
132
133 if (!positiveResult) {
134 // reset the state on cancel, either to expect PUK2 or PIN2
135 if (!mIsPuk2Locked) {
136 resetPinChangeState();
137 } else {
138 resetPinChangeStateForPUK2();
139 }
140 return;
141 }
142
143 // Progress through the dialog states, generally in this order:
144 // 1. Enter old pin
145 // 2. Enter new pin
146 // 3. Re-Enter new pin
147 // While handling any error conditions that may show up in between.
148 // Also handle the PUK2 entry, if it is requested.
149 //
150 // In general, if any invalid entries are made, the dialog re-
151 // appears with text to indicate what the issue is.
152 switch (mPinChangeState) {
153 case PIN_CHANGE_OLD:
154 mOldPin = mButtonChangePin2.getText();
155 mButtonChangePin2.setText("");
156 // if the pin is not valid, display a message and reset the state.
157 if (validatePin (mOldPin, false)) {
158 mPinChangeState = PIN_CHANGE_NEW;
159 displayPinChangeDialog();
160 } else {
161 displayPinChangeDialog(R.string.invalidPin2, true);
162 }
163 break;
164 case PIN_CHANGE_NEW:
165 mNewPin = mButtonChangePin2.getText();
166 mButtonChangePin2.setText("");
167 // if the new pin is not valid, display a message and reset the state.
168 if (validatePin (mNewPin, false)) {
169 mPinChangeState = PIN_CHANGE_REENTER;
170 displayPinChangeDialog();
171 } else {
172 displayPinChangeDialog(R.string.invalidPin2, true);
173 }
174 break;
175 case PIN_CHANGE_REENTER:
176 // if the re-entered pin is not valid, display a message and reset the state.
177 if (!mNewPin.equals(mButtonChangePin2.getText())) {
178 mPinChangeState = PIN_CHANGE_NEW;
179 mButtonChangePin2.setText("");
180 displayPinChangeDialog(R.string.mismatchPin2, true);
181 } else {
182 // If the PIN is valid, then we submit the change PIN request.
183 mButtonChangePin2.setText("");
184 Message onComplete = mFDNHandler.obtainMessage(
185 EVENT_PIN2_CHANGE_COMPLETE);
186 mPhone.getIccCard().changeIccFdnPassword(
187 mOldPin, mNewPin, onComplete);
188 }
189 break;
190 case PIN_CHANGE_PUK: {
191 // Doh! too many incorrect requests, PUK requested.
192 mPuk2 = mButtonChangePin2.getText();
193 mButtonChangePin2.setText("");
194 // if the puk is not valid, display
195 // a message and reset the state.
196 if (validatePin (mPuk2, true)) {
197 mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
198 displayPinChangeDialog();
199 } else {
200 displayPinChangeDialog(R.string.invalidPuk2, true);
201 }
202 }
203 break;
204 case PIN_CHANGE_NEW_PIN_FOR_PUK:
205 mNewPin = mButtonChangePin2.getText();
206 mButtonChangePin2.setText("");
207 // if the new pin is not valid, display
208 // a message and reset the state.
209 if (validatePin (mNewPin, false)) {
210 mPinChangeState = PIN_CHANGE_REENTER_PIN_FOR_PUK;
211 displayPinChangeDialog();
212 } else {
213 displayPinChangeDialog(R.string.invalidPin2, true);
214 }
215 break;
216 case PIN_CHANGE_REENTER_PIN_FOR_PUK:
217 // if the re-entered pin is not valid, display
218 // a message and reset the state.
219 if (!mNewPin.equals(mButtonChangePin2.getText())) {
220 mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
221 mButtonChangePin2.setText("");
222 displayPinChangeDialog(R.string.mismatchPin2, true);
223 } else {
224 // Both puk2 and new pin2 are ready to submit
225 mButtonChangePin2.setText("");
226 Message onComplete = mFDNHandler.obtainMessage(
227 EVENT_PIN2_CHANGE_COMPLETE);
228 mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin, onComplete);
229 }
230 break;
231 }
232 }
233
234 /**
235 * Handler for asynchronous replies from the sim.
236 */
237 private final Handler mFDNHandler = new Handler() {
238 @Override
239 public void handleMessage(Message msg) {
240 switch (msg.what) {
241
242 // when we are enabling FDN, either we are unsuccessful and display
243 // a toast, or just update the UI.
244 case EVENT_PIN2_ENTRY_COMPLETE: {
245 AsyncResult ar = (AsyncResult) msg.obj;
Wink Saville9de0f752013-10-22 19:04:03 -0700246 if (ar.exception != null && ar.exception instanceof CommandException) {
247 int attemptsRemaining = msg.arg1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700248 // see if PUK2 is requested and alert the user accordingly.
Wink Saville9de0f752013-10-22 19:04:03 -0700249 CommandException.Error e =
250 ((CommandException) ar.exception).getCommandError();
251 switch (e) {
252 case SIM_PUK2:
253 // make sure we set the PUK2 state so that we can skip
254 // some redundant behaviour.
255 displayMessage(R.string.fdn_enable_puk2_requested,
256 attemptsRemaining);
257 resetPinChangeStateForPUK2();
258 break;
259 case PASSWORD_INCORRECT:
260 displayMessage(R.string.pin2_invalid, attemptsRemaining);
261 break;
262 default:
263 displayMessage(R.string.fdn_failed, attemptsRemaining);
264 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700265 }
266 }
267 updateEnableFDN();
268 }
269 break;
270
271 // when changing the pin we need to pay attention to whether or not
272 // the error requests a PUK (usually after too many incorrect tries)
273 // Set the state accordingly.
274 case EVENT_PIN2_CHANGE_COMPLETE: {
275 if (DBG)
276 log("Handle EVENT_PIN2_CHANGE_COMPLETE");
277 AsyncResult ar = (AsyncResult) msg.obj;
278 if (ar.exception != null) {
Wink Saville9de0f752013-10-22 19:04:03 -0700279 int attemptsRemaining = msg.arg1;
280 log("Handle EVENT_PIN2_CHANGE_COMPLETE attemptsRemaining="
281 + attemptsRemaining);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700282 CommandException ce = (CommandException) ar.exception;
283 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
284 // throw an alert dialog on the screen, displaying the
285 // request for a PUK2. set the cancel listener to
286 // FdnSetting.onCancel().
287 AlertDialog a = new AlertDialog.Builder(FdnSetting.this)
288 .setMessage(R.string.puk2_requested)
289 .setCancelable(true)
290 .setOnCancelListener(FdnSetting.this)
291 .create();
292 a.getWindow().addFlags(
293 WindowManager.LayoutParams.FLAG_DIM_BEHIND);
294 a.show();
295 } else {
296 // set the correct error message depending upon the state.
297 // Reset the state depending upon or knowledge of the PUK state.
298 if (!mIsPuk2Locked) {
Wink Saville9de0f752013-10-22 19:04:03 -0700299 displayMessage(R.string.badPin2, attemptsRemaining);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700300 resetPinChangeState();
301 } else {
Wink Saville9de0f752013-10-22 19:04:03 -0700302 displayMessage(R.string.badPuk2, attemptsRemaining);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700303 resetPinChangeStateForPUK2();
304 }
305 }
306 } else {
Wink Saville9de0f752013-10-22 19:04:03 -0700307 if (mPinChangeState == PIN_CHANGE_PUK) {
308 displayMessage(R.string.pin2_unblocked);
309 } else {
310 displayMessage(R.string.pin2_changed);
311 }
312
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700313 // reset to normal behaviour on successful change.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 resetPinChangeState();
315 }
316 }
317 break;
318 }
319 }
320 };
321
322 /**
323 * Cancel listener for the PUK2 request alert dialog.
324 */
325 @Override
326 public void onCancel(DialogInterface dialog) {
327 // set the state of the preference and then display the dialog.
328 resetPinChangeStateForPUK2();
329 displayPinChangeDialog(0, true);
330 }
331
332 /**
333 * Display a toast for message, like the rest of the settings.
334 */
Wink Saville9de0f752013-10-22 19:04:03 -0700335 private final void displayMessage(int strId, int attemptsRemaining) {
336 String s = getString(strId);
337 if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) ||
338 (strId == R.string.pin2_invalid)) {
339 if (attemptsRemaining >= 0) {
340 s = getString(strId) + getString(R.string.pin2_attempts, attemptsRemaining);
341 } else {
342 s = getString(strId);
343 }
344 }
345 log("displayMessage: attemptsRemaining=" + attemptsRemaining + " s=" + s);
346 Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
347 }
348
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700349 private final void displayMessage(int strId) {
Wink Saville9de0f752013-10-22 19:04:03 -0700350 displayMessage(strId, -1);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700351 }
352
353 /**
354 * The next two functions are for updating the message field on the dialog.
355 */
356 private final void displayPinChangeDialog() {
357 displayPinChangeDialog(0, true);
358 }
359
360 private final void displayPinChangeDialog(int strId, boolean shouldDisplay) {
361 int msgId;
362 switch (mPinChangeState) {
363 case PIN_CHANGE_OLD:
364 msgId = R.string.oldPin2Label;
365 break;
366 case PIN_CHANGE_NEW:
367 case PIN_CHANGE_NEW_PIN_FOR_PUK:
368 msgId = R.string.newPin2Label;
369 break;
370 case PIN_CHANGE_REENTER:
371 case PIN_CHANGE_REENTER_PIN_FOR_PUK:
372 msgId = R.string.confirmPin2Label;
373 break;
374 case PIN_CHANGE_PUK:
375 default:
376 msgId = R.string.label_puk2_code;
377 break;
378 }
379
380 // append the note / additional message, if needed.
381 if (strId != 0) {
382 mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId));
383 } else {
384 mButtonChangePin2.setDialogMessage(msgId);
385 }
386
387 // only display if requested.
388 if (shouldDisplay) {
389 mButtonChangePin2.showPinDialog();
390 }
391 }
392
393 /**
394 * Reset the state of the pin change dialog.
395 */
396 private final void resetPinChangeState() {
397 if (DBG) log("resetPinChangeState");
398 mPinChangeState = PIN_CHANGE_OLD;
399 displayPinChangeDialog(0, false);
400 mOldPin = mNewPin = "";
401 mIsPuk2Locked = false;
402 }
403
404 /**
405 * Reset the state of the pin change dialog solely for PUK2 use.
406 */
407 private final void resetPinChangeStateForPUK2() {
408 if (DBG) log("resetPinChangeStateForPUK2");
409 mPinChangeState = PIN_CHANGE_PUK;
410 displayPinChangeDialog(0, false);
411 mOldPin = mNewPin = mPuk2 = "";
412 mIsPuk2Locked = true;
413 }
414
415 /**
416 * Validate the pin entry.
417 *
418 * @param pin This is the pin to validate
419 * @param isPuk Boolean indicating whether we are to treat
420 * the pin input as a puk.
421 */
422 private boolean validatePin(String pin, boolean isPuk) {
423
424 // for pin, we have 4-8 numbers, or puk, we use only 8.
425 int pinMinimum = isPuk ? MAX_PIN_LENGTH : MIN_PIN_LENGTH;
426
427 // check validity
428 if (pin == null || pin.length() < pinMinimum || pin.length() > MAX_PIN_LENGTH) {
429 return false;
430 } else {
431 return true;
432 }
433 }
434
435 /**
436 * Reflect the updated FDN state in the UI.
437 */
438 private void updateEnableFDN() {
439 if (mPhone.getIccCard().getIccFdnEnabled()) {
440 mButtonEnableFDN.setTitle(R.string.enable_fdn_ok);
441 mButtonEnableFDN.setSummary(R.string.fdn_enabled);
442 mButtonEnableFDN.setDialogTitle(R.string.disable_fdn);
443 } else {
444 mButtonEnableFDN.setTitle(R.string.disable_fdn_ok);
445 mButtonEnableFDN.setSummary(R.string.fdn_disabled);
446 mButtonEnableFDN.setDialogTitle(R.string.enable_fdn);
447 }
448 }
449
450 @Override
451 protected void onCreate(Bundle icicle) {
452 super.onCreate(icicle);
453
454 addPreferencesFromResource(R.xml.fdn_setting);
455
456 mPhone = PhoneGlobals.getPhone();
457
458 //get UI object references
459 PreferenceScreen prefSet = getPreferenceScreen();
460 mButtonEnableFDN = (EditPinPreference) prefSet.findPreference(BUTTON_FDN_ENABLE_KEY);
461 mButtonChangePin2 = (EditPinPreference) prefSet.findPreference(BUTTON_CHANGE_PIN2_KEY);
462
463 //assign click listener and update state
464 mButtonEnableFDN.setOnPinEnteredListener(this);
465 updateEnableFDN();
466
467 mButtonChangePin2.setOnPinEnteredListener(this);
468
469 // Only reset the pin change dialog if we're not in the middle of changing it.
470 if (icicle == null) {
471 resetPinChangeState();
472 } else {
473 mIsPuk2Locked = icicle.getBoolean(SKIP_OLD_PIN_KEY);
474 mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY);
475 mOldPin = icicle.getString(OLD_PIN_KEY);
476 mNewPin = icicle.getString(NEW_PIN_KEY);
477 mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY));
478 mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY));
479 }
480
481 ActionBar actionBar = getActionBar();
482 if (actionBar != null) {
483 // android.R.id.home will be triggered in onOptionsItemSelected()
484 actionBar.setDisplayHomeAsUpEnabled(true);
485 }
486 }
487
488 @Override
489 protected void onResume() {
490 super.onResume();
491 mPhone = PhoneGlobals.getPhone();
492 updateEnableFDN();
493 }
494
495 /**
496 * Save the state of the pin change.
497 */
498 @Override
499 protected void onSaveInstanceState(Bundle out) {
500 super.onSaveInstanceState(out);
501 out.putBoolean(SKIP_OLD_PIN_KEY, mIsPuk2Locked);
502 out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState);
503 out.putString(OLD_PIN_KEY, mOldPin);
504 out.putString(NEW_PIN_KEY, mNewPin);
505 out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
506 out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
507 }
508
509 @Override
510 public boolean onOptionsItemSelected(MenuItem item) {
511 final int itemId = item.getItemId();
512 if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled()
513 CallFeaturesSetting.goUpToTopLevelSetting(this);
514 return true;
515 }
516 return super.onOptionsItemSelected(item);
517 }
518
519 private void log(String msg) {
520 Log.d(LOG_TAG, "FdnSetting: " + msg);
521 }
522}
523