bcc89a423bd0db39797c7750e69d5660ce4e2e3e
[android/platform/packages/apps/Phone.git] / src / com / android / phone / CallFeaturesSetting.java
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
17 package com.android.phone;
18
19 import com.android.internal.telephony.Phone;
20 import com.android.internal.telephony.PhoneFactory;
21 import com.android.internal.telephony.gsm.CallForwardInfo;
22 import com.android.internal.telephony.gsm.CommandsInterface;
23
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.app.ProgressDialog;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.database.Cursor;
30 import android.os.AsyncResult;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.preference.CheckBoxPreference;
35 import android.preference.ListPreference;
36 import android.preference.Preference;
37 import android.preference.PreferenceActivity;
38 import android.preference.PreferenceScreen;
39 import android.provider.Contacts.PhonesColumns;
40 import android.provider.Settings;
41 import android.telephony.PhoneNumberUtils;
42 import android.telephony.ServiceState;
43 import android.text.TextUtils;
44 import android.util.Log;
45 import android.view.WindowManager;
46
47 public class CallFeaturesSetting extends PreferenceActivity
48         implements DialogInterface.OnClickListener,
49         Preference.OnPreferenceChangeListener,
50         EditPhoneNumberPreference.OnDialogClosedListener,
51         EditPhoneNumberPreference.GetDefaultNumberListener{
52
53     // intent action for this activity.
54     public static final String ACTION_ADD_VOICEMAIL =
55         "com.android.phone.CallFeaturesSetting.ADD_VOICEMAIL";
56
57     // debug data
58     private static final String LOG_TAG = "call features settings";
59     private static final boolean DBG = false;
60
61     // string contants
62     private static final String NUM_PROJECTION[] = {PhonesColumns.NUMBER};
63     private static final String SRC_TAGS[]       = {"{0}"};
64
65     // String keys for preference lookup
66     private static final String BUTTON_CLIR_KEY  = "button_clir_key";
67     private static final String BUTTON_CW_KEY    = "button_cw_key";
68     private static final String BUTTON_CFU_KEY   = "button_cfu_key";
69     private static final String BUTTON_CFB_KEY   = "button_cfb_key";
70     private static final String BUTTON_CFNRY_KEY = "button_cfnry_key";
71     private static final String BUTTON_CFNRC_KEY = "button_cfnrc_key";
72     private static final String BUTTON_VOICEMAIL_KEY = "button_voicemail_key";
73     private static final String BUTTON_FDN_KEY   = "button_fdn_key";
74
75     // used to store the state of expanded preferences
76     private static final String BUTTON_CF_EXPAND_KEY = "button_cf_expand_key";
77     private static final String BUTTON_MORE_EXPAND_KEY = "button_more_expand_key";
78
79     private static final String SUMMARY_CFU_KEY   = "summary_cfu_key";
80     private static final String SUMMARY_CFB_KEY   = "summary_cfb_key";
81     private static final String SUMMARY_CFNRY_KEY = "summary_cfnry_key";
82     private static final String SUMMARY_CFNRC_KEY = "summary_cfnrc_key";
83
84     private static final String APP_STATE_KEY     = "app_state_key";
85     private static final String DISPLAY_MODE_KEY  = "display_mode_key";
86
87     private Intent mContactListIntent;
88     private Intent mFDNSettingIntent;
89
90     // events
91     private static final int EVENT_SERVICE_STATE_CHANGED = 100;
92     private static final int EVENT_CLIR_EXECUTED         = 200;
93     private static final int EVENT_CW_EXECUTED           = 300;
94     private static final int EVENT_CF_EXECUTED           = 400;
95     /** Event for Async voicemail change call */
96     private static final int EVENT_VOICEMAIL_CHANGED     = 500;
97     /** track the query cancel event. */
98     private static final int EVENT_INITAL_QUERY_CANCELED = 600;
99
100     /** Handle to voicemail pref */
101     private static final int VOICEMAIL_PREF_ID = CommandsInterface.CF_REASON_NOT_REACHABLE + 1;
102
103     private Phone mPhone;
104
105     private static final int BUSY_DIALOG = 100;
106     private static final int EXCEPTION_ERROR = 200;
107     private static final int RESPONSE_ERROR = 300;
108     private static final int VM_NOCHANGE_ERROR = 400;
109     private static final int VM_RESPONSE_ERROR = 500;
110
111     /** used to track errors with the radio off. */
112     private static final int RADIO_OFF_ERROR = 800;
113     private static final int INITIAL_BUSY_DIALOG = 900;
114
115     // dialog identifiers for voicemail
116     private static final int VOICEMAIL_DIALOG_CONFIRM = 600;
117     private static final int VOICEMAIL_DIALOG_PROGRESS = 700;
118
119     // status message sent back from handlers
120     //  handleGetCLIRMessage
121     //  handleGetCWMessage
122     //  handleGetCFMessage
123     private static final int MSG_OK = 100;
124     private static final int MSG_EXCEPTION = 200;
125     private static final int MSG_UNEXPECTED_RESPONSE = 300;
126     // special statuses for voicemail controls.
127     private static final int MSG_VM_EXCEPTION = 400;
128     private static final int MSG_VM_BUSY = 500;
129     private static final int MSG_VM_OK = 600;
130     private static final int MSG_VM_NOCHANGE = 700;
131     private static final int MSG_RADIO_OFF = 800;
132
133     // application states including network error state.
134     // this includes seperate state for the inital query, which is cancelable.
135     private enum AppState {
136         INPUT_READY,
137         DIALOG_OPEN,
138         WAITING_NUMBER_SELECT,
139         BUSY_NETWORK_CONNECT,
140         NETWORK_ERROR,
141         INITIAL_QUERY
142     };
143     private AppState mAppState;
144
145     /** Additional state tracking to handle expanded views (lazy queries)*/
146     private static final int DISP_MODE_MAIN = -1;
147     private static final int DISP_MODE_CF = -2;
148     private static final int DISP_MODE_MORE = -3;
149     private int mDisplayMode;
150     private boolean mCFDataStale = true;
151     private boolean mMoreDataStale = true;
152     private boolean mIsBusyDialogAvailable = false;
153
154     // toggle buttons
155     private PreferenceScreen mSubMenuFDNSettings;
156     private ListPreference mButtonCLIR;
157     private CheckBoxPreference mButtonCW;
158     private EditPhoneNumberPreference mButtonCFU;
159     private EditPhoneNumberPreference mButtonCFB;
160     private EditPhoneNumberPreference mButtonCFNRy;
161     private EditPhoneNumberPreference mButtonCFNRc;
162     private EditPhoneNumberPreference mSubMenuVoicemailSettings;
163     private PreferenceScreen mButtonCFExpand;
164     private PreferenceScreen mButtonMoreExpand;
165
166     // cf number strings
167     private String mDialingNumCFU;
168     private String mDialingNumCFB;
169     private String mDialingNumCFNRy;
170     private String mDialingNumCFNRc;
171     /** string to hold old voicemail number as it is being updated. */
172     private String mOldVmNumber;
173
174
175     /*
176      * Click Listeners, handle click based on objects attached to UI.
177      */
178
179     // Click listener for all toggle events
180     @Override
181     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
182         if (mAppState != AppState.INPUT_READY) {
183             if (DBG) {
184                 log("onPreferencesHierarchyClick: preference request denied, currently busy.");
185             }
186             return false;
187         }
188
189         if (DBG) log("onPreferencesHierarchyClick: request preference click.");
190
191         AppState nextState = AppState.INPUT_READY;
192
193         if (preference == mButtonCW) {
194             handleCWClickRequest(mButtonCW.isChecked());
195             nextState = AppState.BUSY_NETWORK_CONNECT;
196
197         } else if (preference == mButtonCLIR) {
198             // let the normal listpreference UI take care of this.
199             return false;
200
201         } else if ((preference instanceof EditPhoneNumberPreference) &&
202                 ((preference == mButtonCFU) || (preference == mButtonCFB) ||
203                 (preference == mButtonCFNRy) || (preference == mButtonCFNRc) ||
204                 (preference == mSubMenuVoicemailSettings))) {
205             nextState = AppState.DIALOG_OPEN;
206         } else if (preference == mSubMenuFDNSettings) {
207             // let the intent handler from the caller take care of the
208             // navigation to the FDN screen.
209             return false;
210
211         /** perform the requested expansion, and query the network.*/
212         } else if (preference == mButtonCFExpand){
213             setDisplayMode(DISP_MODE_CF);
214             return true;
215         } else if (preference == mButtonMoreExpand){
216             setDisplayMode(DISP_MODE_MORE);
217         }
218
219         if (nextState != AppState.INPUT_READY) {
220             setAppState(nextState);
221             return true;
222         }
223
224         return false;
225     }
226
227     /**
228      * Implemented to support onPreferenceChangeListener to look for preference
229      * changes specifically on CLIR.
230      *
231      * @param preference is the preference to be changed, should be mButtonCLIR.
232      * @param objValue should be the value of the selection, NOT its localized
233      * display value.
234      */
235     public boolean onPreferenceChange(Preference preference, Object objValue) {
236         if (preference == mButtonCLIR) {
237             // send the command and update state.
238             handleCLIRClickRequest(mButtonCLIR.findIndexOfValue((String) objValue));
239             setAppState(AppState.BUSY_NETWORK_CONNECT);
240         }
241
242         // always let the preference setting proceed.
243         return true;
244     }
245
246
247     /**
248      * Perform the query request for the expanded items upon user request.
249      */
250     public void setDisplayMode(int displayMode) {
251         mDisplayMode = displayMode;
252
253         // look for the data if it is considered stale.
254         if ((mCFDataStale && (displayMode == DISP_MODE_CF)) ||
255                 (mMoreDataStale && (displayMode == DISP_MODE_MORE))){
256             if (DBG) log("setDisplayMode: performing requested expansion.");
257
258             // If airplane mode is on, do not bother querying.
259             if (Settings.System.getInt(getContentResolver(),
260                     Settings.System.AIRPLANE_MODE_ON, 0) <= 0) {
261                 // query state if radio is available
262                 //  if its out of service, just wait for the radio to be ready
263                 //  if its neither of these states, throw up an error.
264                 setAppState(AppState.INITIAL_QUERY);
265
266                 int radioState = mPhone.getServiceState().getState();
267
268                 if (radioState == ServiceState.STATE_IN_SERVICE) {
269                     // Query ONLY what we are currently expanding.
270                     if (displayMode == DISP_MODE_CF) {
271                         queryAllCFOptions();
272                     } else {
273                         queryMoreOptions();
274                     }
275                 } else if (radioState == ServiceState.STATE_POWER_OFF){
276                     if (DBG) log("onCreate: radio not ready, waiting for signal.");
277                     mPhone.registerForServiceStateChanged(mNetworkServiceHandler,
278                             EVENT_SERVICE_STATE_CHANGED, null);
279                 } else {
280                     setAppState(AppState.NETWORK_ERROR, MSG_EXCEPTION);
281                 }
282             } else {
283                 if (DBG) log("setDisplayMode: radio is off!");
284                 setAppState(AppState.NETWORK_ERROR, MSG_RADIO_OFF);
285             }
286         }
287     }
288
289     // Preference click listener invoked on OnDialogClosed for EditPhoneNumberPreference.
290     public void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked) {
291         if (mAppState != AppState.DIALOG_OPEN) {
292             if (DBG) {
293                 log("onPreferenceClick: preference request denied, currently busy.");
294             }
295             return;
296         } else if (buttonClicked == DialogInterface.BUTTON2) {
297             // Button2 is the cancel button.
298             setAppState (AppState.INPUT_READY);
299             return;
300         }
301
302         if (DBG) log("onPreferenceClick: request preference click on dialog close.");
303
304         AppState nextState = AppState.INPUT_READY;
305
306         if (preference instanceof EditPhoneNumberPreference) {
307             EditPhoneNumberPreference epn = preference;
308
309             if (epn == mSubMenuVoicemailSettings) {
310                 handleVMBtnClickRequest();
311
312             } else {
313                 int reason = 0;
314                 int time = 0;
315                 String number = "";
316                 // We use CommandsInterface.CF_ACTION_REGISTRATION for both the Enable
317                 // and Update (Button1) functions.
318                 int action = (epn.isToggled() || (buttonClicked == DialogInterface.BUTTON1)) ?
319                         CommandsInterface.CF_ACTION_REGISTRATION :
320                         CommandsInterface.CF_ACTION_DISABLE;
321
322                 // The formatted string seems to be giving the MMI codes some problems,
323                 // so we strip the formatting first before sending the number.
324                 number = PhoneNumberUtils.stripSeparators((epn.getPhoneNumber()));
325                 if (epn == mButtonCFU) {
326                     nextState = AppState.BUSY_NETWORK_CONNECT;
327                     reason = CommandsInterface.CF_REASON_UNCONDITIONAL;
328                     mDialingNumCFU = number;
329                 } else if (epn == mButtonCFB) {
330                     nextState = AppState.BUSY_NETWORK_CONNECT;
331                     reason = CommandsInterface.CF_REASON_BUSY;
332                     mDialingNumCFB = number;
333                 } else if (epn == mButtonCFNRy) {
334                     nextState = AppState.BUSY_NETWORK_CONNECT;
335                     reason = CommandsInterface.CF_REASON_NO_REPLY;
336                     time = 20;
337                     mDialingNumCFNRy = number;
338                 } else if (epn == mButtonCFNRc) {
339                     nextState = AppState.BUSY_NETWORK_CONNECT;
340                     reason = CommandsInterface.CF_REASON_NOT_REACHABLE;
341                     mDialingNumCFNRc = number;
342                 }
343
344                 if (nextState == AppState.BUSY_NETWORK_CONNECT) {
345                     handleCFBtnClickRequest(action, reason, time, number);
346                 }
347
348                 if (nextState != AppState.DIALOG_OPEN) {
349                     setAppState(nextState);
350                 }
351             }
352         }
353     }
354
355     /**
356      * Implemented for EditPhoneNumberPreference.GetDefaultNumberListener.
357      * This method set the default values for the various
358      * EditPhoneNumberPreference dialogs.
359      */
360     public String onGetDefaultNumber(EditPhoneNumberPreference preference) {
361         if (preference == mSubMenuVoicemailSettings) {
362             // update the voicemail number field, which takes care of the
363             // mSubMenuVoicemailSettings itself, so we should return null.
364             if (DBG) log("updating default for voicemail dialog");
365             updateVoiceNumberField();
366             return null;
367         }
368
369         String vmDisplay = PhoneFactory.getDefaultPhone().getVoiceMailNumber();
370         if (TextUtils.isEmpty(vmDisplay)) {
371             // if there is no voicemail number, we just return null to
372             // indicate no contribution.
373             return null;
374         }
375
376         // Return the voicemail number prepended with "VM: "
377         if (DBG) log("updating default for call forwarding dialogs");
378         return getString(R.string.voicemail_abbreviated) + " " + vmDisplay;
379     }
380
381
382     // override the startsubactivity call to make changes in state consistent.
383     @Override
384     public void startActivityForResult(Intent intent, int requestCode) {
385         if (requestCode == -1) {
386             // this is an intent requested from the preference framework.
387             super.startActivityForResult(intent, requestCode);
388             return;
389         }
390
391         if (mAppState != AppState.DIALOG_OPEN) {
392             if (DBG) {
393                 log("startSubActivity: dialog start activity request denied, currently busy.");
394             }
395             return;
396         }
397
398         if (DBG) log("startSubActivity: starting requested subactivity");
399
400         super.startActivityForResult(intent, requestCode);
401
402         setAppState (AppState.WAITING_NUMBER_SELECT);
403     }
404
405     // asynchronous result call after contacts are selected.
406     @Override
407     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
408         // there are cases where the contact picker may end up sending us more than one
409         // request.  We want to ignore the request if we're not in the correct state.
410         if (mAppState != AppState.WAITING_NUMBER_SELECT) {
411             if (DBG) log("onActivityResult: wrong state, ignoring message from contact picker.");
412             return;
413         } else {
414             setAppState(AppState.DIALOG_OPEN);
415         }
416
417         if (resultCode != RESULT_OK) {
418             if (DBG) log("onActivityResult: contact picker result not OK.");
419             return;
420         }
421
422         Cursor cursor = getContentResolver().query(data.getData(),
423                 NUM_PROJECTION, null, null, null);
424         if ((cursor == null) || (!cursor.moveToFirst())) {
425             if (DBG) log("onActivityResult: bad contact data, no results found.");
426             return;
427         }
428
429         switch (requestCode) {
430             case CommandsInterface.CF_REASON_UNCONDITIONAL:
431                 mButtonCFU.onPickActivityResult(cursor.getString(0));
432                 break;
433             case CommandsInterface.CF_REASON_BUSY:
434                 mButtonCFB.onPickActivityResult(cursor.getString(0));
435                 break;
436             case CommandsInterface.CF_REASON_NO_REPLY:
437                 mButtonCFNRy.onPickActivityResult(cursor.getString(0));
438                 break;
439             case CommandsInterface.CF_REASON_NOT_REACHABLE:
440                 mButtonCFNRc.onPickActivityResult(cursor.getString(0));
441                 break;
442             case VOICEMAIL_PREF_ID:
443                 mSubMenuVoicemailSettings.onPickActivityResult(cursor.getString(0));
444                 break;
445             default:
446                 // TODO: may need exception here.
447         }
448
449     }
450
451     // CLIR object
452     private void handleCLIRClickRequest(int i) {
453         if (DBG) log("handleCLIRClickRequest: requesting set Call Line Id Restriction (CLIR) to " +
454                 (i == CommandsInterface.CLIR_INVOCATION ? "ENABLE" :
455                     (i == CommandsInterface.CLIR_SUPPRESSION ? "DISABLE" : "NETWORK DEFAULT")));
456         mPhone.setOutgoingCallerIdDisplay(i,
457                 Message.obtain(mSetOptionComplete, EVENT_CLIR_EXECUTED));
458     }
459
460     // CW object
461     private void handleCWClickRequest(boolean b) {
462         if (DBG) log("handleCWClickRequest: requesting set call waiting enable (CW) to" +
463                 Boolean.toString(b));
464         mPhone.setCallWaiting(b, Message.obtain(mSetOptionComplete, EVENT_CW_EXECUTED));
465     }
466
467     // CF Button objects
468     private void handleCFBtnClickRequest(int action, int reason, int time, String number) {
469         if (DBG) log("handleCFBtnClickRequest: requesting set call forwarding (CF) " +
470                 Integer.toString(reason) + " to " + Integer.toString(action) + " with number " +
471                 number);
472         mPhone.setCallForwardingOption(action,
473                 reason,
474                 number,
475                 time,
476                 Message.obtain(mSetOptionComplete, EVENT_CF_EXECUTED, reason, 0));
477     }
478
479     // Voicemail button logic
480     private void handleVMBtnClickRequest() {
481         // normally called on the dialog close.
482
483         // Since we're stripping the formatting out on the getPhoneNumber()
484         // call now, we won't need to do so here anymore.
485         String newVMNumber = mSubMenuVoicemailSettings.getPhoneNumber();
486
487         // empty vm number == clearing the vm number ?
488         if (newVMNumber == null) {
489             newVMNumber = "";
490         }
491
492         //throw a warning if they are the same.
493         if (newVMNumber.equals(mOldVmNumber)) {
494             setAppState(AppState.INPUT_READY, MSG_VM_NOCHANGE);
495             return;
496         }
497
498         // otherwise, set it.
499         setAppState (AppState.BUSY_NETWORK_CONNECT, MSG_VM_BUSY);
500         if (DBG) log("save voicemail #: " + newVMNumber);
501         mPhone.setVoiceMailNumber(
502                 mPhone.getVoiceMailAlphaTag().toString(),
503                 newVMNumber,
504                 Message.obtain(mSetOptionComplete, EVENT_VOICEMAIL_CHANGED));
505     }
506
507     /*
508      * Callback to handle option update completions
509      */
510
511     // **Callback on option setting when complete.
512     private Handler mSetOptionComplete = new Handler() {
513         @Override
514         public void handleMessage(Message msg) {
515             // query to make sure we're looking at the same data as that in the network.
516             switch (msg.what) {
517                 case EVENT_CLIR_EXECUTED:
518                     handleSetCLIRMessage();
519                     break;
520                 case EVENT_CW_EXECUTED:
521                     handleSetCWMessage();
522                     break;
523                 case EVENT_CF_EXECUTED:
524                     handleSetCFMessage(msg.arg1, (AsyncResult) msg.obj);
525                     break;
526                 case EVENT_VOICEMAIL_CHANGED:
527                     handleSetVMMessage((AsyncResult) msg.obj);
528                     break;
529                 default:
530                     // TODO: should never reach this, may want to throw exception
531             }
532         }
533     };
534
535     // CLIR Object
536     private void handleSetCLIRMessage() {
537         if (DBG) {
538             log("handleSetCLIRMessage: set CLIR request complete, reading value from network.");
539         }
540         mPhone.getOutgoingCallerIdDisplay(Message.obtain(mGetOptionComplete, EVENT_CLIR_EXECUTED));
541     }
542
543     // CW Object
544     private void handleSetCWMessage() {
545         if (DBG) {
546             log("handleSetCWMessage: set CW request complete, reading value back from network.");
547         }
548         mPhone.getCallWaiting(Message.obtain(mGetOptionComplete, EVENT_CW_EXECUTED));
549     }
550
551     // CF Objects
552     private void handleSetCFMessage(int reason, AsyncResult r) {
553         if (DBG) {
554             log("handleSetCFMessage: set CF request complete, reading value back from network.");
555         }
556
557         // handle the exception in the set function's async result by
558         // propagating it to the getCallForwarding function.  This is
559         // so that we can display the error AFTER the setting has gone
560         // through the standard (set/get) cycle.
561         mPhone.getCallForwardingOption(reason,
562                 Message.obtain(mGetOptionComplete, EVENT_CF_EXECUTED, reason, 0, r.exception));
563     }
564
565     // Voicemail Object
566     private void handleSetVMMessage(AsyncResult ar) {
567         if (DBG) {
568             log("handleSetVMMessage: set VM request complete");
569         }
570         if (ar.exception == null) {
571             if (DBG) log("change VM success!");
572             setAppState(AppState.INPUT_READY, MSG_VM_OK);
573         } else {
574             // TODO: may want to check the exception and branch on it.
575             if (DBG) log("change VM failed!");
576             setAppState(AppState.NETWORK_ERROR, MSG_VM_EXCEPTION);
577         }
578         updateVoiceNumberField();
579     }
580
581
582     /*
583      * Callback to handle query completions
584      */
585
586     // **Callback on option getting when complete.
587     private Handler mGetOptionComplete = new Handler() {
588         @Override
589         public void handleMessage(Message msg) {
590             boolean bHandled = false;
591             int status = MSG_OK;
592             switch (msg.what) {
593                 case EVENT_CLIR_EXECUTED:
594                     status = handleGetCLIRMessage((AsyncResult) msg.obj);
595                     bHandled = true;
596                     break;
597                 case EVENT_CW_EXECUTED:
598                     status = handleGetCWMessage((AsyncResult) msg.obj);
599                     bHandled = true;
600                     break;
601                 case EVENT_CF_EXECUTED:
602                     status = handleGetCFMessage((AsyncResult) msg.obj, msg.arg1);
603                     bHandled = true;
604                     break;
605                 default:
606                     // TODO: should never reach this, may want to throw exception
607             }
608             if (status != MSG_OK) {
609                 setAppState(AppState.NETWORK_ERROR, status);
610             } else if (bHandled) {
611                 setAppState(AppState.INPUT_READY);
612             }
613         }
614     };
615
616     // CLIR Object
617     private int handleGetCLIRMessage(AsyncResult ar) {
618         // done with query, display the new settings.
619         if (ar.exception != null) {
620             if (DBG) log("handleGetCLIRMessage: Error getting CLIR enable state.");
621             return MSG_EXCEPTION;
622         } else {
623             int clirArray[] = (int[]) ar.result;
624             if (clirArray.length != 2) {
625                 if (DBG) log("handleGetCLIRMessage: Error getting CLIR state, unexpected value.");
626                 return MSG_UNEXPECTED_RESPONSE;
627             } else {
628                 if (DBG) log("handleGetCLIRMessage: CLIR enable state successfully queried.");
629                 syncCLIRUIState(clirArray);
630             }
631         }
632         return MSG_OK;
633     }
634
635     // CW Object
636     private int handleGetCWMessage(AsyncResult ar) {
637         if (ar.exception != null) {
638             if (DBG) log("handleGetCWMessage: Error getting CW enable state.");
639             return MSG_EXCEPTION;
640         } else {
641             if (DBG) log("handleGetCWMessage: CW enable state successfully queried.");
642             syncCWState((int[]) ar.result);
643         }
644         return MSG_OK;
645     }
646
647     // CF Object
648     private int handleGetCFMessage(AsyncResult ar, int reason) {
649         // done with query, display the new settings.
650         if (ar.exception != null) {
651             if (DBG) log("handleGetCFMessage: Error getting CF enable state.");
652             return MSG_EXCEPTION;
653         } else if (ar.userObj instanceof Throwable) {
654             // TODO: I don't think it makes sense to throw the error up to
655             // the user, but this may be reconsidered.  For now, just log
656             // the specific error and throw up a generic error.
657             if (DBG) log("handleGetCFMessage: Error during set call, reason: " + reason +
658                     " exception: " + ((Throwable) ar.userObj).toString());
659             return MSG_UNEXPECTED_RESPONSE;
660         } else {
661             CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
662             if (cfInfoArray.length == 0) {
663                 if (DBG) log("handleGetCFMessage: Error getting CF state, unexpected value.");
664                 return MSG_UNEXPECTED_RESPONSE;
665             } else {
666                 // TODO: look through the information for the voice data
667                 // in reality, we should probably take the other service
668                 // classes into account, but this may be more than we
669                 // want to expose to the user.
670                 for (int i = 0, length = cfInfoArray.length; i < length; i++) {
671                     if ((CommandsInterface.SERVICE_CLASS_VOICE &
672                             cfInfoArray[i].serviceClass) != 0) {
673                         if (DBG) {
674                             log("handleGetCFMessage: CF state successfully queried for reason " +
675                                 Integer.toBinaryString(reason));
676                         }
677                         syncCFUIState(reason, cfInfoArray[i]);
678                         break;
679                     }
680                 }
681             }
682         }
683         return MSG_OK;
684     }
685
686
687     /*
688      * Methods used to sync UI state with that of the network
689      */
690
691     // set the state of the UI based on CW State
692     private void syncCWState(int cwArray[]) {
693         if (DBG) log("syncCWState: Setting UI state consistent with CW enable state of " +
694                 ((cwArray[0] == 1) ? "ENABLED" : "DISABLED"));
695         mButtonCW.setChecked(cwArray[0] == 1);
696     }
697
698     /**
699      * The logic in this method is based upon the code in {@link CommandsInterface#getCLIR()}.
700      *
701      * @param clirArgs is the int[2] retrieved from the getCLIR response, please refer to
702      * the link above more more details.
703      */
704     private void syncCLIRUIState(int clirArgs[]) {
705         if (DBG) log("syncCLIRUIState: Setting UI state consistent with CLIR.");
706
707         // enable if the setting is valid.
708         final boolean enabled = clirArgs[1] == 1 || clirArgs[1] == 3 || clirArgs[1] == 4;
709         mButtonCLIR.setEnabled(enabled);
710
711         // set the value of the preference based upon the clirArgs.
712         int value = CommandsInterface.CLIR_DEFAULT;
713         switch (clirArgs[1]) {
714             case 1: // Permanently provisioned
715             case 3: // Temporary presentation disallowed
716             case 4: // Temporary presentation allowed
717                 switch (clirArgs[0]) {
718                     case 1: // CLIR invoked
719                         value = CommandsInterface.CLIR_INVOCATION;
720                         break;
721                     case 2: // CLIR suppressed
722                         value = CommandsInterface.CLIR_SUPPRESSION;
723                         break;
724                     case 0: // Network default
725                     default:
726                         value = CommandsInterface.CLIR_DEFAULT;
727                         break;
728                 }
729                 break;
730             case 0: // Not Provisioned
731             case 2: // Unknown (network error, etc)
732             default:
733                 value = CommandsInterface.CLIR_DEFAULT;
734                 break;
735         }
736         setButtonCLIRValue(value);
737     }
738
739     /**
740      * Helper function to set both the value and the summary of the CLIR preference.
741      */
742     private void setButtonCLIRValue (int value) {
743         
744         if (mButtonCLIR == null) {
745             return;
746         }
747         
748         // first, set the value.
749         mButtonCLIR.setValueIndex(value);
750
751         // set the string summary to reflect the value
752         int summary = R.string.sum_default_caller_id;
753         switch (value) {
754             case CommandsInterface.CLIR_SUPPRESSION:
755                 summary = R.string.sum_show_caller_id;
756                 break;
757             case CommandsInterface.CLIR_INVOCATION:
758                 summary = R.string.sum_hide_caller_id;
759                 break;
760             case CommandsInterface.CLIR_DEFAULT:
761                 summary = R.string.sum_default_caller_id;
762                 break;
763         }
764         mButtonCLIR.setSummary(summary);
765     }
766
767     // called by syncCFUIState to do repetitive changes to UI button state.
768     private void adjustCFbuttonState(EditPhoneNumberPreference epn,
769             boolean isActive, int template, String number) {
770         
771         if (epn == null) {
772             return;
773         }
774         
775         CharSequence summaryOn = "";
776
777         if (isActive) {
778             if (number != null) {
779                 String values[] = {number};
780                 summaryOn = TextUtils.replace(getText(template), SRC_TAGS, values);
781             }
782             epn.setSummaryOn(summaryOn);
783         }
784
785         epn.setToggled(isActive);
786         epn.setPhoneNumber(number);
787     }
788
789     // set the state of the UI based on CF State
790     private void syncCFUIState(int reason, CallForwardInfo info) {
791         boolean active = (info.status == 1);
792         switch (reason) {
793             case CommandsInterface.CF_REASON_UNCONDITIONAL:
794                 if (DBG) log("syncCFUIState: Setting UI state consistent with CFU.");
795                 adjustCFbuttonState(mButtonCFU, active, R.string.sum_cfu_enabled, info.number);
796                 mDialingNumCFU = info.number;
797                 break;
798             case CommandsInterface.CF_REASON_BUSY:
799                 if (DBG) log("syncCFUIState: Setting UI state consistent with CFB.");
800                 adjustCFbuttonState(mButtonCFB, active, R.string.sum_cfb_enabled, info.number);
801                 mDialingNumCFB = info.number;
802                 break;
803             case CommandsInterface.CF_REASON_NO_REPLY:
804                 if (DBG) log("syncCFUIState: Setting UI state consistent with CFNRy.");
805                 adjustCFbuttonState(mButtonCFNRy, active, R.string.sum_cfnry_enabled, info.number);
806                 mDialingNumCFNRy = info.number;
807                 break;
808             case CommandsInterface.CF_REASON_NOT_REACHABLE:
809                 if (DBG) log("syncCFUIState: Setting UI state consistent with CFNRc.");
810                 adjustCFbuttonState(mButtonCFNRc, active, R.string.sum_cfnrc_enabled, info.number);
811                 mDialingNumCFNRc = info.number;
812                 break;
813         }
814     }
815
816     // update the voicemail number from what we've recorded on the sim.
817     private void updateVoiceNumberField() {
818         if (mSubMenuVoicemailSettings == null) {
819             return;
820         }
821         
822         mOldVmNumber = mPhone.getVoiceMailNumber();
823         if (mOldVmNumber == null) {
824             mOldVmNumber = "";
825         }
826         mSubMenuVoicemailSettings.setPhoneNumber(mOldVmNumber);
827     }
828
829
830     /*
831      * Helper Methods for Activity class.
832      * The inital query commands are split into two pieces now
833      * for individual expansion.  This combined with the ability
834      * to cancel queries allows for a much better user experience,
835      * and also ensures that the user only waits to update the
836      * data that is relevant.
837      */
838
839     // Handler to track service availability.
840     private Handler mNetworkServiceHandler = new Handler() {
841         @Override
842         public void handleMessage(Message msg) {
843             switch (msg.what) {
844                 case EVENT_SERVICE_STATE_CHANGED: {
845                         ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
846                         if (state.getState() == ServiceState.STATE_IN_SERVICE) {
847                             if (DBG) {
848                                 log("mNetworkServiceHandler: network available for queries.");
849                             }
850                             // Query ONLY what we are interested in now.
851                             switch (mDisplayMode) {
852                                 case DISP_MODE_CF:
853                                     queryAllCFOptions();
854                                     break;
855                                 case DISP_MODE_MORE:
856                                     queryMoreOptions();
857                                     break;
858                             }
859
860                             mPhone.unregisterForServiceStateChanged(mNetworkServiceHandler);
861                         }
862                     }
863                     break;
864                 case EVENT_INITAL_QUERY_CANCELED:
865                     if (DBG) log("mNetworkServiceHandler: cancel query requested.");
866                     dismissExpandedDialog();
867                     break;
868             }
869         }
870     };
871
872     // Request to begin querying for all options.
873     private void queryAllCFOptions() {
874         if (DBG) log("queryAllCFOptions: begin querying call features.");
875         mPhone.getCallForwardingOption(CommandsInterface.CF_REASON_UNCONDITIONAL,
876                 Message.obtain(mGetAllCFOptionsComplete, EVENT_CF_EXECUTED,
877                         CommandsInterface.CF_REASON_UNCONDITIONAL, 0));
878     }
879
880     // callback after each step of querying for all options.
881     private Handler mGetAllCFOptionsComplete = new Handler() {
882         @Override
883         public void handleMessage(Message msg) {
884
885             AsyncResult ar = (AsyncResult) msg.obj;
886             int status = MSG_OK;
887
888             switch (msg.what) {
889                 case EVENT_CF_EXECUTED:
890                     status = handleGetCFMessage(ar, msg.arg1);
891                     int nextReason = -1;
892                     switch (msg.arg1) {
893                         case CommandsInterface.CF_REASON_UNCONDITIONAL:
894                             if (DBG) log("mGetAllOptionsComplete: CFU query done, querying CFB.");
895                             nextReason = CommandsInterface.CF_REASON_BUSY;
896                             break;
897                         case CommandsInterface.CF_REASON_BUSY:
898                             if (DBG) {
899                                 log("mGetAllOptionsComplete: CFB query done, querying CFNRy.");
900                             }
901                             nextReason = CommandsInterface.CF_REASON_NO_REPLY;
902                             break;
903                         case CommandsInterface.CF_REASON_NO_REPLY:
904                             if (DBG) {
905                                 log("mGetAllOptionsComplete: CFNRy query done, querying CFNRc.");
906                             }
907                             nextReason = CommandsInterface.CF_REASON_NOT_REACHABLE;
908                             break;
909                         case CommandsInterface.CF_REASON_NOT_REACHABLE:
910                             if (DBG) {
911                                 log("mGetAllOptionsComplete: CFNRc query done, querying CLIR.");
912                             }
913                             break;
914                         default:
915                             // TODO: should never reach this, may want to throw exception
916                     }
917                     if (status != MSG_OK) {
918                         setAppState(AppState.NETWORK_ERROR, status);
919                     } else {
920                         if (nextReason != -1) {
921                             mPhone.getCallForwardingOption(nextReason,
922                                     Message.obtain(mGetAllCFOptionsComplete, EVENT_CF_EXECUTED,
923                                             nextReason, 0));
924                         } else {
925                             mCFDataStale = false;
926                             setAppState(AppState.INPUT_READY);
927                         }
928                     }
929                     break;
930
931                 default:
932                     // TODO: should never reach this, may want to throw exception
933                     break;
934             }
935         }
936     };
937
938     // Request to begin querying for all options.
939     private void queryMoreOptions() {
940         if (DBG) log("queryMoreOptions: begin querying call features.");
941         mPhone.getOutgoingCallerIdDisplay(
942                 Message.obtain(mGetMoreOptionsComplete, EVENT_CLIR_EXECUTED));
943     }
944
945     // callback after each step of querying for all options.
946     private Handler mGetMoreOptionsComplete = new Handler() {
947         @Override
948         public void handleMessage(Message msg) {
949
950             AsyncResult ar = (AsyncResult) msg.obj;
951             int status = MSG_OK;
952
953             switch (msg.what) {
954                 case EVENT_CLIR_EXECUTED:
955                     status = handleGetCLIRMessage(ar);
956                     if (DBG) log("mGetAllOptionsComplete: CLIR query done, querying CW.");
957                     if (status != MSG_OK) {
958                         setAppState(AppState.NETWORK_ERROR, status);
959                     } else {
960                         mPhone.getCallWaiting(
961                                 Message.obtain(mGetMoreOptionsComplete, EVENT_CW_EXECUTED));
962                     }
963                     break;
964
965                 case EVENT_CW_EXECUTED:
966                     status = handleGetCWMessage(ar);
967                     if (DBG) {
968                         log("mGetAllOptionsComplete: CW query done, all call features queried.");
969                     }
970                     if (status != MSG_OK) {
971                         setAppState(AppState.NETWORK_ERROR, status);
972                     } else {
973                         mMoreDataStale = false;
974                         setAppState(AppState.INPUT_READY);
975                     }
976                     break;
977
978                 default:
979                     // TODO: should never reach this, may want to throw exception
980                     break;
981             }
982         }
983     };
984
985     // dialog creation method, called by showDialog()
986     @Override
987     protected Dialog onCreateDialog(int id) {
988
989         if ((id == BUSY_DIALOG) || (id == VOICEMAIL_DIALOG_PROGRESS) ||
990                 (id == INITIAL_BUSY_DIALOG)) {
991             ProgressDialog dialog = new ProgressDialog(this);
992             dialog.setTitle(getText(R.string.updating_title));
993             dialog.setIndeterminate(true);
994
995             switch (id) {
996                 case BUSY_DIALOG:
997                     mIsBusyDialogAvailable = true;
998                     dialog.setCancelable(false);
999                     dialog.setMessage(getText(R.string.updating_settings));
1000                     break;
1001                 case VOICEMAIL_DIALOG_PROGRESS:
1002                     dialog.setCancelable(false);
1003                     dialog.setMessage(getText(R.string.vm_save_number));
1004                     break;
1005                 case INITIAL_BUSY_DIALOG:
1006                     // Allowing the user to cancel on the initial query.
1007                     dialog.setCancelable(true);
1008                     dialog.setCancelMessage(
1009                             mNetworkServiceHandler.obtainMessage(EVENT_INITAL_QUERY_CANCELED));
1010                     dialog.setMessage(getText(R.string.reading_settings));
1011                     break;
1012             }
1013             // make the dialog more obvious by bluring the background.
1014             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
1015
1016             return dialog;
1017
1018         // Handle error dialog codes
1019         } else if ((id == RESPONSE_ERROR) || (id == EXCEPTION_ERROR) ||
1020                 (id == VM_RESPONSE_ERROR) || (id == VM_NOCHANGE_ERROR) ||
1021                 (id == VOICEMAIL_DIALOG_CONFIRM) || (id == RADIO_OFF_ERROR)){
1022
1023             AlertDialog.Builder b = new AlertDialog.Builder(this);
1024
1025             int msgId;
1026             int titleId = R.string.error_updating_title;
1027             switch (id) {
1028                 case VOICEMAIL_DIALOG_CONFIRM:
1029                     msgId = R.string.vm_changed;
1030                     titleId = R.string.voicemail;
1031                     // Set Button 2
1032                     b.setNegativeButton(R.string.close_dialog, this);
1033                     break;
1034                 case VM_NOCHANGE_ERROR:
1035                     // even though this is technically an error,
1036                     // keep the title friendly.
1037                     msgId = R.string.no_change;
1038                     titleId = R.string.voicemail;
1039                     // Set Button 2
1040                     b.setNegativeButton(R.string.close_dialog, this);
1041                     break;
1042                 case VM_RESPONSE_ERROR:
1043                     msgId = R.string.vm_change_failed;
1044                     // Set Button 1
1045                     b.setPositiveButton(R.string.close_dialog, this);
1046                     break;
1047                 case RESPONSE_ERROR:
1048                     msgId = R.string.response_error;
1049                     // Set Button 2, tells the activity that the error is
1050                     // recoverable on dialog exit.
1051                     b.setNegativeButton(R.string.close_dialog, this);
1052                     break;
1053                 case RADIO_OFF_ERROR:
1054                     msgId = R.string.radio_off_error;
1055                     // Set Button 3
1056                     b.setNeutralButton(R.string.close_dialog, this);
1057                     break;
1058                 case EXCEPTION_ERROR:
1059                 default:
1060                     msgId = R.string.exception_error;
1061                     // Set Button 3, tells the activity that the error is
1062                     // not recoverable on dialog exit.
1063                     b.setNeutralButton(R.string.close_dialog, this);
1064                     break;
1065             }
1066
1067             b.setTitle(getText(titleId));
1068             b.setMessage(getText(msgId));
1069             b.setCancelable(false);
1070             AlertDialog dialog = b.create();
1071
1072             // make the dialog more obvious by bluring the background.
1073             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
1074
1075             return dialog;
1076         }
1077
1078         return null;
1079     }
1080
1081     // This is a method implemented for DialogInterface.OnClickListener.
1082     // Used with the error dialog to close the app, voicemail dialog to just dismiss.
1083     // Close button is mapped to BUTTON1 for the errors that close the activity,
1084     // while those that are mapped to 3 only move the preference focus.
1085     public void onClick(DialogInterface dialog, int which) {
1086         dialog.dismiss();
1087         switch (which){
1088             case DialogInterface.BUTTON3:
1089                 // Neutral Button, used when we want to cancel expansion.
1090                 dismissExpandedDialog();
1091                 break;
1092             case DialogInterface.BUTTON1:
1093                 // Negative Button
1094                 finish();
1095                 break;
1096             default:
1097                 // just let the dialog close and go back to the input
1098                 // ready state
1099                 setAppState (AppState.INPUT_READY);
1100                 // Positive Button
1101         }
1102     }
1103
1104     /** dismiss the expanded dialog view, going back to the main preference view */
1105     private void dismissExpandedDialog() {
1106         // The dialogs that can invoke this method (via onClick()), can ONLY
1107         // be reached when either expanded dialog (More or Call Forwarding)
1108         // is open.  However, the Monkey somehow managed to get to this code
1109         // without the expanded dialogs being available (1305094).  Adding null
1110         // pointer checks just as a good measure.  This should be fine because
1111         // if the expanded dialog is NOT shown, we want to ignore the dismiss
1112         // message and go to INPUT_READY anyway.
1113         switch (mDisplayMode) {
1114             case DISP_MODE_CF:
1115                 if (mButtonCFExpand != null && mButtonCFExpand.getDialog() != null) {
1116                     mButtonCFExpand.getDialog().dismiss();
1117                 }
1118                 break;
1119             case DISP_MODE_MORE:
1120                 if (mButtonMoreExpand != null && mButtonMoreExpand.getDialog() != null) {
1121                     mButtonMoreExpand.getDialog().dismiss();
1122                 }
1123                 break;
1124         }
1125         mDisplayMode = DISP_MODE_MAIN;
1126         setAppState (AppState.INPUT_READY);
1127     }
1128
1129     // set the app state when no error message needs to be set.
1130     private void setAppState(AppState requestedState) throws IllegalStateException{
1131         if (requestedState == AppState.NETWORK_ERROR) {
1132             if (DBG) log("setAppState: illegal error state without reason.");
1133             throw new IllegalStateException ("illegal error state without reason.");
1134         }
1135         setAppState (requestedState, MSG_OK);
1136     }
1137
1138     // set the app state with optional status.
1139     private void setAppState(AppState requestedState, int msgStatus)
1140             throws IllegalStateException{
1141
1142         if (requestedState == mAppState) {
1143             if (DBG) log("setAppState: requestedState same as current state. ignoring.");
1144             return;
1145         }
1146
1147         // handle errors
1148         // make sure we dismiss the correct dialogs.
1149         if (requestedState == AppState.NETWORK_ERROR) {
1150             if (DBG) log("setAppState: " + requestedState + ": " + msgStatus);
1151             switch (msgStatus) {
1152                 case MSG_EXCEPTION:
1153                     if (mAppState == AppState.INITIAL_QUERY) {
1154                         dismissDialog(INITIAL_BUSY_DIALOG);
1155                     } else {
1156                         dismissBusyDialog();
1157                     }
1158                     showDialog (EXCEPTION_ERROR);
1159                     break;
1160                 case MSG_RADIO_OFF:
1161                     showDialog (RADIO_OFF_ERROR);
1162                     break;
1163                 case MSG_UNEXPECTED_RESPONSE:
1164                     if (mAppState == AppState.INITIAL_QUERY) {
1165                         dismissDialog(INITIAL_BUSY_DIALOG);
1166                     } else {
1167                         dismissBusyDialog();
1168                     }
1169                     showDialog (RESPONSE_ERROR);
1170                     break;
1171                 case MSG_VM_EXCEPTION:
1172                     dismissDialog(VOICEMAIL_DIALOG_PROGRESS);
1173                     showDialog (VM_RESPONSE_ERROR);
1174                     break;
1175                 case MSG_OK:
1176                 default:
1177                     // This should never happen.
1178             }
1179             mAppState = requestedState;
1180             return;
1181         }
1182
1183         switch (mAppState) {
1184             // We can now transition out of the NETWORK_ERROR state, when the
1185             // user is moving from the expanded views back to the main view.
1186             case NETWORK_ERROR:
1187                 if (requestedState != AppState.INPUT_READY) {
1188                     if (DBG) log("setAppState: illegal transition from NETWORK_ERROR");
1189                     throw new IllegalStateException
1190                             ("illegal transition from NETWORK_ERROR");
1191                 }
1192                 break;
1193             case INPUT_READY:
1194                 if (DBG) log("setAppState: displaying busy dialog, reason: " + requestedState);
1195                 if (requestedState == AppState.INITIAL_QUERY) {
1196                     showDialog(INITIAL_BUSY_DIALOG);
1197                 } else if (requestedState == AppState.BUSY_NETWORK_CONNECT) {
1198                     showDialog(BUSY_DIALOG);
1199                 } else if (requestedState == AppState.WAITING_NUMBER_SELECT) {
1200                     if (DBG) log("setAppState: illegal transition from INPUT_READY");
1201                     throw new IllegalStateException
1202                             ("illegal transition from INPUT_READY");
1203                 }
1204                 break;
1205             case DIALOG_OPEN:
1206                 if (requestedState == AppState.INPUT_READY) {
1207                     if (msgStatus == MSG_VM_NOCHANGE) {
1208                         showDialog(VM_NOCHANGE_ERROR);
1209                     }
1210                 } else {
1211                     if (msgStatus == MSG_VM_BUSY) {
1212                         showDialog(VOICEMAIL_DIALOG_PROGRESS);
1213                     } else {
1214                         showDialog(BUSY_DIALOG);
1215                     }
1216                 }
1217                 break;
1218             case INITIAL_QUERY:
1219                 // the initial query state can ONLY go to the input ready state.
1220                 if (requestedState != AppState.INPUT_READY) {
1221                     if (DBG) log("setAppState: illegal transition from INITIAL_QUERY");
1222                     throw new IllegalStateException
1223                             ("illegal transition from INITIAL_QUERY");
1224                 }
1225                 dismissDialog(INITIAL_BUSY_DIALOG);
1226                 break;
1227             case BUSY_NETWORK_CONNECT:
1228                 if (requestedState != AppState.INPUT_READY) {
1229                     if (DBG) log("setAppState: illegal transition from BUSY_NETWORK_CONNECT");
1230                     throw new IllegalStateException
1231                             ("illegal transition from BUSY_NETWORK_CONNECT");
1232                 }
1233                 if (msgStatus == MSG_VM_OK) {
1234                     dismissDialog(VOICEMAIL_DIALOG_PROGRESS);
1235                     showDialog(VOICEMAIL_DIALOG_CONFIRM);
1236                 } else {
1237                     dismissBusyDialog();
1238                 }
1239                 break;
1240             case WAITING_NUMBER_SELECT:
1241                 if (requestedState != AppState.DIALOG_OPEN) {
1242                     if (DBG) log("setAppState: illegal transition from WAITING_NUMBER_SELECT");
1243                     throw new IllegalStateException
1244                             ("illegal transition from WAITING_NUMBER_SELECT");
1245                 }
1246                 dismissBusyDialog();
1247                 break;
1248         }
1249         mAppState = requestedState;
1250     }
1251
1252     /**
1253      * Make sure that the busy dialog is available before we try to close it.
1254      * This check needs to be done because the generic busy dialog is used for
1255      * a number of cases, but we need to make sure it has been displayed before
1256      * being dismissed.
1257      */
1258     private final void dismissBusyDialog() {
1259         if (mIsBusyDialogAvailable) {
1260             dismissDialog(BUSY_DIALOG);
1261         }
1262     }
1263
1264     /*
1265      * Activity class methods
1266      */
1267
1268     @Override
1269     protected void onCreate(Bundle icicle) {
1270         super.onCreate(icicle);
1271
1272         addPreferencesFromResource(R.xml.call_feature_setting);
1273
1274         // get buttons
1275         PreferenceScreen prefSet = getPreferenceScreen();
1276         mButtonCLIR  = (ListPreference) prefSet.findPreference(BUTTON_CLIR_KEY);
1277         mButtonCW    = (CheckBoxPreference) prefSet.findPreference(BUTTON_CW_KEY);
1278         mButtonCFU   = (EditPhoneNumberPreference) prefSet.findPreference(BUTTON_CFU_KEY);
1279         mButtonCFB   = (EditPhoneNumberPreference) prefSet.findPreference(BUTTON_CFB_KEY);
1280         mButtonCFNRy = (EditPhoneNumberPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
1281         mButtonCFNRc = (EditPhoneNumberPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
1282         mSubMenuVoicemailSettings = (EditPhoneNumberPreference)
1283                 prefSet.findPreference(BUTTON_VOICEMAIL_KEY);
1284         mSubMenuFDNSettings = (PreferenceScreen) prefSet.findPreference(BUTTON_FDN_KEY);
1285
1286         // get a reference to the Preference Screens for Call Forwarding and "More" settings.
1287         mButtonCFExpand = (PreferenceScreen) prefSet.findPreference(BUTTON_CF_EXPAND_KEY);
1288         mButtonMoreExpand = (PreferenceScreen) prefSet.findPreference(BUTTON_MORE_EXPAND_KEY);
1289
1290         // Set links to the current activity and any UI settings that
1291         // effect the dialog for each preference.  Also set the
1292         // dependencies between the child (CFB, CFNRy, CFNRc)
1293         // preferences and the CFU preference.
1294         if (mButtonCFU != null){
1295             mButtonCFU.setParentActivity(this, CommandsInterface.CF_REASON_UNCONDITIONAL, this);
1296             mButtonCFU.setDialogOnClosedListener(this);
1297             mButtonCFU.setDialogTitle(R.string.labelCF);
1298             mButtonCFU.setDialogMessage(R.string.messageCFU);
1299         }
1300         
1301         if (mButtonCFB != null) {
1302             mButtonCFB.setParentActivity(this, CommandsInterface.CF_REASON_BUSY, this);
1303             mButtonCFB.setDialogOnClosedListener(this);
1304             mButtonCFB.setDependency(BUTTON_CFU_KEY);
1305             mButtonCFB.setDialogTitle(R.string.labelCF);
1306             mButtonCFB.setDialogMessage(R.string.messageCFB);
1307         }
1308         
1309         if (mButtonCFNRy != null) {
1310             mButtonCFNRy.setParentActivity(this, CommandsInterface.CF_REASON_NO_REPLY, this);
1311             mButtonCFNRy.setDialogOnClosedListener(this);
1312             mButtonCFNRy.setDependency(BUTTON_CFU_KEY);
1313             mButtonCFNRy.setDialogTitle(R.string.labelCF);
1314             mButtonCFNRy.setDialogMessage(R.string.messageCFNRy);
1315         }
1316         
1317         if (mButtonCFNRc != null) {
1318             mButtonCFNRc.setParentActivity(this, CommandsInterface.CF_REASON_NOT_REACHABLE, this);
1319             mButtonCFNRc.setDialogOnClosedListener(this);
1320             mButtonCFNRc.setDependency(BUTTON_CFU_KEY);
1321             mButtonCFNRc.setDialogTitle(R.string.labelCF);
1322             mButtonCFNRc.setDialogMessage(R.string.messageCFNRc);
1323         }
1324         
1325         if (mSubMenuVoicemailSettings != null) {
1326             mSubMenuVoicemailSettings.setParentActivity(this, VOICEMAIL_PREF_ID, this);
1327             mSubMenuVoicemailSettings.setDialogOnClosedListener(this);
1328             mSubMenuVoicemailSettings.setDialogTitle(R.string.voicemail_settings_number_label);
1329         }
1330         
1331         // set the listener for the CLIR list preference so we can issue CLIR commands.
1332         if (mButtonCLIR != null ) {
1333             mButtonCLIR.setOnPreferenceChangeListener(this);
1334         }
1335
1336         // create intent to bring up contact list
1337         mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT);
1338         mContactListIntent.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
1339
1340         mFDNSettingIntent = new Intent(Intent.ACTION_MAIN);
1341         mFDNSettingIntent.setClassName(this, FdnSetting.class.getName());
1342         mSubMenuFDNSettings.setIntent (mFDNSettingIntent);
1343
1344         mPhone = PhoneFactory.getDefaultPhone();
1345         mAppState = AppState.INPUT_READY;
1346
1347         if (icicle != null) {
1348             // retrieve number state
1349             mDialingNumCFU = icicle.getString(SUMMARY_CFU_KEY);
1350             mDialingNumCFB = icicle.getString(SUMMARY_CFB_KEY);
1351             mDialingNumCFNRy = icicle.getString(SUMMARY_CFNRY_KEY);
1352             mDialingNumCFNRc = icicle.getString(SUMMARY_CFNRC_KEY);
1353
1354             // reset CF buttons
1355             adjustCFbuttonState(mButtonCFU, icicle.getBoolean(BUTTON_CFU_KEY),
1356                     R.string.sum_cfu_enabled, mDialingNumCFU);
1357             adjustCFbuttonState(mButtonCFB, icicle.getBoolean(BUTTON_CFB_KEY),
1358                     R.string.sum_cfb_enabled, mDialingNumCFB);
1359             adjustCFbuttonState(mButtonCFNRy, icicle.getBoolean(BUTTON_CFNRY_KEY),
1360                     R.string.sum_cfnry_enabled, mDialingNumCFNRy);
1361             adjustCFbuttonState(mButtonCFNRc, icicle.getBoolean(BUTTON_CFNRC_KEY),
1362                     R.string.sum_cfnrc_enabled, mDialingNumCFNRc);
1363
1364             // reset other button state
1365             setButtonCLIRValue(icicle.getInt(BUTTON_CLIR_KEY));
1366             if (mButtonCW != null) {
1367                 mButtonCW.setChecked(icicle.getBoolean(BUTTON_CW_KEY));
1368             }
1369
1370             // set app state
1371             mAppState = (AppState) icicle.getSerializable(APP_STATE_KEY);
1372             mCFDataStale = icicle.getBoolean(BUTTON_CF_EXPAND_KEY);
1373             mMoreDataStale = icicle.getBoolean(BUTTON_MORE_EXPAND_KEY);
1374             mDisplayMode = icicle.getInt(DISPLAY_MODE_KEY);
1375
1376         } else {
1377             // The queries here are now lazily done, and all data is assumed stale
1378             // when we first start the activity.
1379             mCFDataStale = true;
1380             mMoreDataStale = true;
1381
1382             // check the intent that started this activity and pop up the voicemail
1383             // dialog if we've been asked to.
1384             if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) {
1385                 setAppState(AppState.DIALOG_OPEN);
1386                 mSubMenuVoicemailSettings.showPhoneNumberDialog();
1387             }
1388         }
1389
1390         updateVoiceNumberField();
1391     }
1392
1393     @Override
1394     protected void onSaveInstanceState(Bundle outState) {
1395         super.onSaveInstanceState(outState);
1396
1397         if (DBG) log("onSaveInstanceState: saving relevant UI state.");
1398
1399         // save button state
1400         if (mButtonCLIR != null) {
1401             outState.putInt(BUTTON_CLIR_KEY, mButtonCLIR.findIndexOfValue(mButtonCLIR.getValue()));
1402         }
1403         if (mButtonCW != null) {
1404             outState.putBoolean(BUTTON_CW_KEY, mButtonCW.isChecked());
1405         }
1406         if (mButtonCFU != null) {
1407             outState.putBoolean(BUTTON_CFU_KEY, mButtonCFU.isToggled());
1408         }
1409         if (mButtonCFB != null) {
1410             outState.putBoolean(BUTTON_CFB_KEY, mButtonCFB.isToggled());
1411         }
1412         if (mButtonCFNRy != null) {
1413             outState.putBoolean(BUTTON_CFNRY_KEY, mButtonCFNRy.isToggled());
1414         }
1415         if (mButtonCFNRc != null) {
1416             outState.putBoolean(BUTTON_CFNRC_KEY, mButtonCFNRc.isToggled());
1417         }
1418
1419         // save number state
1420         outState.putString(SUMMARY_CFU_KEY, mDialingNumCFU);
1421         outState.putString(SUMMARY_CFB_KEY, mDialingNumCFB);
1422         outState.putString(SUMMARY_CFNRY_KEY, mDialingNumCFNRy);
1423         outState.putString(SUMMARY_CFNRC_KEY, mDialingNumCFNRc);
1424
1425         // save state of the app
1426         outState.putSerializable(APP_STATE_KEY, mAppState);
1427         outState.putBoolean(BUTTON_CF_EXPAND_KEY, mCFDataStale);
1428         outState.putBoolean(BUTTON_MORE_EXPAND_KEY, mMoreDataStale);
1429         outState.putInt(DISPLAY_MODE_KEY, mDisplayMode);
1430     }
1431
1432     private static void log(String msg) {
1433         Log.d(LOG_TAG, msg);
1434     }
1435 }