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