Introduce vibration options for voicemail notification
[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.CallForwardInfo;
20 import com.android.internal.telephony.CommandsInterface;
21 import com.android.internal.telephony.Phone;
22 import com.android.internal.telephony.cdma.TtyIntent;
23 import com.android.phone.sip.SipSharedPreferences;
24
25 import android.app.ActionBar;
26 import android.app.Activity;
27 import android.app.AlertDialog;
28 import android.app.Dialog;
29 import android.app.ProgressDialog;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.content.SharedPreferences;
34 import android.content.SharedPreferences.Editor;
35 import android.content.pm.ActivityInfo;
36 import android.content.pm.PackageManager;
37 import android.content.pm.ResolveInfo;
38 import android.database.Cursor;
39 import android.media.AudioManager;
40 import android.net.sip.SipManager;
41 import android.os.AsyncResult;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.Message;
45 import android.preference.CheckBoxPreference;
46 import android.preference.ListPreference;
47 import android.preference.Preference;
48 import android.preference.PreferenceActivity;
49 import android.preference.PreferenceGroup;
50 import android.preference.PreferenceScreen;
51 import android.provider.ContactsContract.CommonDataKinds;
52 import android.provider.Settings;
53 import android.telephony.PhoneNumberUtils;
54 import android.text.TextUtils;
55 import android.util.Log;
56 import android.view.MenuItem;
57 import android.view.WindowManager;
58 import android.widget.ListAdapter;
59
60 import java.util.Collection;
61 import java.util.HashMap;
62 import java.util.HashSet;
63 import java.util.Iterator;
64 import java.util.List;
65 import java.util.Map;
66
67 /**
68  * Top level "Call settings" UI; see res/xml/call_feature_setting.xml
69  *
70  * This preference screen is the root of the "Call settings" hierarchy
71  * available from the Phone app; the settings here let you control various
72  * features related to phone calls (including voicemail settings, SIP
73  * settings, the "Respond via SMS" feature, and others.)  It's used only
74  * on voice-capable phone devices.
75  *
76  * Note that this activity is part of the package com.android.phone, even
77  * though you reach it from the "Phone" app (i.e. DialtactsActivity) which
78  * is from the package com.android.contacts.
79  *
80  * For the "Mobile network settings" screen under the main Settings app,
81  * see apps/Phone/src/com/android/phone/Settings.java.
82  */
83 public class CallFeaturesSetting extends PreferenceActivity
84         implements DialogInterface.OnClickListener,
85         Preference.OnPreferenceChangeListener,
86         EditPhoneNumberPreference.OnDialogClosedListener,
87         EditPhoneNumberPreference.GetDefaultNumberListener{
88
89     // intent action to bring up voice mail settings
90     public static final String ACTION_ADD_VOICEMAIL =
91         "com.android.phone.CallFeaturesSetting.ADD_VOICEMAIL";
92     // intent action sent by this activity to a voice mail provider
93     // to trigger its configuration UI
94     public static final String ACTION_CONFIGURE_VOICEMAIL =
95         "com.android.phone.CallFeaturesSetting.CONFIGURE_VOICEMAIL";
96     // Extra put in the return from VM provider config containing voicemail number to set
97     public static final String VM_NUMBER_EXTRA = "com.android.phone.VoicemailNumber";
98     // Extra put in the return from VM provider config containing call forwarding number to set
99     public static final String FWD_NUMBER_EXTRA = "com.android.phone.ForwardingNumber";
100     // Extra put in the return from VM provider config containing call forwarding number to set
101     public static final String FWD_NUMBER_TIME_EXTRA = "com.android.phone.ForwardingNumberTime";
102     // If the VM provider returns non null value in this extra we will force the user to
103     // choose another VM provider
104     public static final String SIGNOUT_EXTRA = "com.android.phone.Signout";
105     //Information about logical "up" Activity
106     private static final String UP_ACTIVITY_PACKAGE = "com.android.contacts";
107     private static final String UP_ACTIVITY_CLASS =
108             "com.android.contacts.activities.DialtactsActivity";
109
110     // Used to tell the saving logic to leave forwarding number as is
111     public static final CallForwardInfo[] FWD_SETTINGS_DONT_TOUCH = null;
112     // Suffix appended to provider key for storing vm number
113     public static final String VM_NUMBER_TAG = "#VMNumber";
114     // Suffix appended to provider key for storing forwarding settings
115     public static final String FWD_SETTINGS_TAG = "#FWDSettings";
116     // Suffix appended to forward settings key for storing length of settings array
117     public static final String FWD_SETTINGS_LENGTH_TAG = "#Length";
118     // Suffix appended to forward settings key for storing an individual setting
119     public static final String FWD_SETTING_TAG = "#Setting";
120     // Suffixes appended to forward setting key for storing an individual setting properties
121     public static final String FWD_SETTING_STATUS = "#Status";
122     public static final String FWD_SETTING_REASON = "#Reason";
123     public static final String FWD_SETTING_NUMBER = "#Number";
124     public static final String FWD_SETTING_TIME = "#Time";
125
126     // Key identifying the default vocie mail provider
127     public static final String DEFAULT_VM_PROVIDER_KEY = "";
128
129     // Extra put into ACTION_ADD_VOICEMAIL call to indicate which provider
130     // to remove from the list of providers presented to the user
131     public static final String IGNORE_PROVIDER_EXTRA = "com.android.phone.ProviderToIgnore";
132
133     // debug data
134     private static final String LOG_TAG = "CallFeaturesSetting";
135     private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2);
136
137     // string constants
138     private static final String NUM_PROJECTION[] = {CommonDataKinds.Phone.NUMBER};
139
140     // String keys for preference lookup
141     // TODO: Naming these "BUTTON_*" is confusing since they're not actually buttons(!)
142     private static final String BUTTON_VOICEMAIL_KEY = "button_voicemail_key";
143     private static final String BUTTON_VOICEMAIL_PROVIDER_KEY = "button_voicemail_provider_key";
144     private static final String BUTTON_VOICEMAIL_SETTING_KEY = "button_voicemail_setting_key";
145     /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY =
146             "button_voicemail_notification_vibrate_when_key";
147     private static final String BUTTON_FDN_KEY   = "button_fdn_key";
148     private static final String BUTTON_RESPOND_VIA_SMS_KEY   = "button_respond_via_sms_key";
149
150     private static final String BUTTON_DTMF_KEY   = "button_dtmf_settings";
151     private static final String BUTTON_RETRY_KEY  = "button_auto_retry_key";
152     private static final String BUTTON_TTY_KEY    = "button_tty_mode_key";
153     private static final String BUTTON_HAC_KEY    = "button_hac_key";
154
155     private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
156     private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
157
158     private static final String VM_NUMBERS_SHARED_PREFERENCES_NAME = "vm_numbers";
159
160     private static final String BUTTON_SIP_CALL_OPTIONS =
161             "sip_call_options_key";
162     private static final String BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY =
163             "sip_call_options_wifi_only_key";
164     private static final String SIP_SETTINGS_CATEGORY_KEY =
165             "sip_settings_category_key";
166
167     private Intent mContactListIntent;
168
169     /** Event for Async voicemail change call */
170     private static final int EVENT_VOICEMAIL_CHANGED        = 500;
171     private static final int EVENT_FORWARDING_CHANGED       = 501;
172     private static final int EVENT_FORWARDING_GET_COMPLETED = 502;
173
174     // preferred TTY mode
175     // Phone.TTY_MODE_xxx
176     static final int preferredTtyMode = Phone.TTY_MODE_OFF;
177
178     // Dtmf tone types
179     static final int DTMF_TONE_TYPE_NORMAL = 0;
180     static final int DTMF_TONE_TYPE_LONG   = 1;
181
182     public static final String HAC_KEY = "HACSetting";
183     public static final String HAC_VAL_ON = "ON";
184     public static final String HAC_VAL_OFF = "OFF";
185
186     /** Handle to voicemail pref */
187     private static final int VOICEMAIL_PREF_ID = 1;
188     private static final int VOICEMAIL_PROVIDER_CFG_ID = 2;
189
190     private Phone mPhone;
191
192     private AudioManager mAudioManager;
193     private SipManager mSipManager;
194
195     private static final int VM_NOCHANGE_ERROR = 400;
196     private static final int VM_RESPONSE_ERROR = 500;
197     private static final int FW_SET_RESPONSE_ERROR = 501;
198     private static final int FW_GET_RESPONSE_ERROR = 502;
199
200
201     // dialog identifiers for voicemail
202     private static final int VOICEMAIL_DIALOG_CONFIRM = 600;
203     private static final int VOICEMAIL_FWD_SAVING_DIALOG = 601;
204     private static final int VOICEMAIL_FWD_READING_DIALOG = 602;
205     private static final int VOICEMAIL_REVERTING_DIALOG = 603;
206
207     // status message sent back from handlers
208     private static final int MSG_OK = 100;
209
210     // special statuses for voicemail controls.
211     private static final int MSG_VM_EXCEPTION = 400;
212     private static final int MSG_FW_SET_EXCEPTION = 401;
213     private static final int MSG_FW_GET_EXCEPTION = 402;
214     private static final int MSG_VM_OK = 600;
215     private static final int MSG_VM_NOCHANGE = 700;
216
217     private EditPhoneNumberPreference mSubMenuVoicemailSettings;
218
219     private CheckBoxPreference mButtonAutoRetry;
220     private CheckBoxPreference mButtonHAC;
221     private ListPreference mButtonDTMF;
222     private ListPreference mButtonTTY;
223     private ListPreference mButtonSipCallOptions;
224     private ListPreference mVoicemailProviders;
225     private PreferenceScreen mVoicemailSettings;
226     private ListPreference mVoicemailNotificationVibrateWhen;
227     private SipSharedPreferences mSipSharedPreferences;
228
229     private class VoiceMailProvider {
230         public VoiceMailProvider(String name, Intent intent) {
231             this.name = name;
232             this.intent = intent;
233         }
234         public String name;
235         public Intent intent;
236     }
237
238     /**
239      * Forwarding settings we are going to save.
240      */
241     static final int [] FORWARDING_SETTINGS_REASONS = new int[] {
242         CommandsInterface.CF_REASON_UNCONDITIONAL,
243         CommandsInterface.CF_REASON_BUSY,
244         CommandsInterface.CF_REASON_NO_REPLY,
245         CommandsInterface.CF_REASON_NOT_REACHABLE
246     };
247
248     private class VoiceMailProviderSettings {
249         /**
250          * Constructs settings object, setting all conditional forwarding to the specified number
251          */
252         public VoiceMailProviderSettings(String voicemailNumber, String forwardingNumber,
253                 int timeSeconds) {
254             this.voicemailNumber = voicemailNumber;
255             if (forwardingNumber == null || forwardingNumber.length() == 0) {
256                 this.forwardingSettings = FWD_SETTINGS_DONT_TOUCH;
257             } else {
258                 this.forwardingSettings = new CallForwardInfo[FORWARDING_SETTINGS_REASONS.length];
259                 for (int i = 0; i < this.forwardingSettings.length; i++) {
260                     CallForwardInfo fi = new CallForwardInfo();
261                     this.forwardingSettings[i] = fi;
262                     fi.reason = FORWARDING_SETTINGS_REASONS[i];
263                     fi.status = (fi.reason == CommandsInterface.CF_REASON_UNCONDITIONAL) ? 0 : 1;
264                     fi.serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
265                     fi.toa = PhoneNumberUtils.TOA_International;
266                     fi.number = forwardingNumber;
267                     fi.timeSeconds = timeSeconds;
268                 }
269             }
270         }
271
272         public VoiceMailProviderSettings(String voicemailNumber, CallForwardInfo[] infos) {
273             this.voicemailNumber = voicemailNumber;
274             this.forwardingSettings = infos;
275         }
276
277         @Override
278         public boolean equals(Object o) {
279             if (o == null) return false;
280             if (!(o instanceof VoiceMailProviderSettings)) return false;
281             final VoiceMailProviderSettings v = (VoiceMailProviderSettings)o;
282
283             return ((this.voicemailNumber == null &&
284                         v.voicemailNumber == null) ||
285                     this.voicemailNumber != null &&
286                         this.voicemailNumber.equals(v.voicemailNumber))
287                     &&
288                     forwardingSettingsEqual(this.forwardingSettings,
289                             v.forwardingSettings);
290         }
291
292         private boolean forwardingSettingsEqual(CallForwardInfo[] infos1,
293                 CallForwardInfo[] infos2) {
294             if (infos1 == infos2) return true;
295             if (infos1 == null || infos2 == null) return false;
296             if (infos1.length != infos2.length) return false;
297             for (int i = 0; i < infos1.length; i++) {
298                 CallForwardInfo i1 = infos1[i];
299                 CallForwardInfo i2 = infos2[i];
300                 if (i1.status != i2.status ||
301                     i1.reason != i2.reason ||
302                     i1.serviceClass != i2.serviceClass ||
303                     i1.toa != i2.toa ||
304                     i1.number != i2.number ||
305                     i1.timeSeconds != i2.timeSeconds) {
306                     return false;
307                 }
308             }
309             return true;
310         }
311
312         @Override
313         public String toString() {
314             return voicemailNumber + ((forwardingSettings != null ) ? (", " +
315                     forwardingSettings.toString()) : "");
316         }
317
318         public String voicemailNumber;
319         public CallForwardInfo[] forwardingSettings;
320     }
321
322     SharedPreferences mPerProviderSavedVMNumbers;
323
324     /**
325      * Results of reading forwarding settings
326      */
327     CallForwardInfo[] mForwardingReadResults = null;
328
329     /**
330      * Result of forwarding number change.
331      * Keys are reasons (eg. unconditional forwarding).
332      */
333     private Map<Integer, AsyncResult> mForwardingChangeResults = null;
334
335     /**
336      * Expected CF read result types.
337      * This set keeps track of the CF types for which we've issued change
338      * commands so we can tell when we've received all of the responses.
339      */
340     private Collection<Integer> mExpectedChangeResultReasons = null;
341
342     /**
343      * Result of vm number change
344      */
345     AsyncResult mVoicemailChangeResult = null;
346
347     /**
348      * Previous VM provider setting so we can return to it in case of failure.
349      */
350     String mPreviousVMProviderKey = null;
351
352     /**
353      * Id of the dialog being currently shown.
354      */
355     int mCurrentDialogId = 0;
356
357     /**
358      * Flag indicating that we are invoking settings for the voicemail provider programmatically
359      * due to vm provider change.
360      */
361     boolean mVMProviderSettingsForced = false;
362
363     /**
364      * Flag indicating that we are making changes to vm or fwd numbers
365      * due to vm provider change.
366      */
367     boolean mChangingVMorFwdDueToProviderChange = false;
368
369     /**
370      * True if we are in the process of vm & fwd number change and vm has already been changed.
371      * This is used to decide what to do in case of rollback.
372      */
373     boolean mVMChangeCompletedSuccesfully = false;
374
375     /**
376      * True if we had full or partial failure setting forwarding numbers and so need to roll them
377      * back.
378      */
379     boolean mFwdChangesRequireRollback = false;
380
381     /**
382      * Id of error msg to display to user once we are done reverting the VM provider to the previous
383      * one.
384      */
385     int mVMOrFwdSetError = 0;
386
387     /**
388      * Data about discovered voice mail settings providers.
389      * Is populated by querying which activities can handle ACTION_CONFIGURE_VOICEMAIL.
390      * They key in this map is package name + activity name.
391      * We always add an entry for the default provider with a key of empty
392      * string and intent value of null.
393      * @see #initVoiceMailProviders.
394      */
395     private final Map<String, VoiceMailProvider> mVMProvidersData =
396         new HashMap<String, VoiceMailProvider>();
397
398     /** string to hold old voicemail number as it is being updated. */
399     private String mOldVmNumber;
400
401     // New call forwarding settings and vm number we will be setting
402     // Need to save these since before we get to saving we need to asynchronously
403     // query the existing forwarding settings.
404     private CallForwardInfo[] mNewFwdSettings;
405     String mNewVMNumber;
406
407     private boolean mForeground;
408
409     @Override
410     public void onPause() {
411         super.onPause();
412         mForeground = false;
413     }
414
415     /**
416      * We have to pull current settings from the network for all kinds of
417      * voicemail providers so we can tell whether we have to update them,
418      * so use this bit to keep track of whether we're reading settings for the
419      * default provider and should therefore save them out when done.
420      */
421     private boolean mReadingSettingsForDefaultProvider = false;
422
423     /*
424      * Click Listeners, handle click based on objects attached to UI.
425      */
426
427     // Click listener for all toggle events
428     @Override
429     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
430         if (preference == mSubMenuVoicemailSettings) {
431             return true;
432         } else if (preference == mButtonDTMF) {
433             return true;
434         } else if (preference == mButtonTTY) {
435             return true;
436         } else if (preference == mButtonAutoRetry) {
437             android.provider.Settings.System.putInt(mPhone.getContext().getContentResolver(),
438                     android.provider.Settings.System.CALL_AUTO_RETRY,
439                     mButtonAutoRetry.isChecked() ? 1 : 0);
440             return true;
441         } else if (preference == mButtonHAC) {
442             int hac = mButtonHAC.isChecked() ? 1 : 0;
443             // Update HAC value in Settings database
444             Settings.System.putInt(mPhone.getContext().getContentResolver(),
445                     Settings.System.HEARING_AID, hac);
446
447             // Update HAC Value in AudioManager
448             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
449             return true;
450         } else if (preference == mVoicemailSettings && preference.getIntent() != null) {
451             if (DBG) log("Invoking cfg intent " + preference.getIntent().getPackage());
452             this.startActivityForResult(preference.getIntent(), VOICEMAIL_PROVIDER_CFG_ID);
453             return true;
454         }
455         return false;
456     }
457
458     /**
459      * Implemented to support onPreferenceChangeListener to look for preference
460      * changes.
461      *
462      * @param preference is the preference to be changed
463      * @param objValue should be the value of the selection, NOT its localized
464      * display value.
465      */
466     public boolean onPreferenceChange(Preference preference, Object objValue) {
467         if (preference == mButtonDTMF) {
468             int index = mButtonDTMF.findIndexOfValue((String) objValue);
469             Settings.System.putInt(mPhone.getContext().getContentResolver(),
470                     Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
471         } else if (preference == mButtonTTY) {
472             handleTTYChange(preference, objValue);
473         } else if (preference == mVoicemailProviders) {
474             final String currentProviderKey = getCurrentVoicemailProviderKey();
475             final String newProviderKey = (String)objValue;
476             if (DBG) log("VM provider changes to " + newProviderKey + " from " +
477                     mPreviousVMProviderKey);
478             if (mPreviousVMProviderKey.equals(newProviderKey)) {
479                 if (DBG) log("No change ");
480                 return true;
481             }
482             updateVMPreferenceWidgets(newProviderKey);
483
484             mPreviousVMProviderKey = currentProviderKey;
485
486             final VoiceMailProviderSettings newProviderSettings =
487                     loadSettingsForVoiceMailProvider(newProviderKey);
488
489             // If the user switches to a voice mail provider and we have a
490             // numbers stored for it we will automatically change the
491             // phone's
492             // voice mail and forwarding number to the stored ones.
493             // Otherwise we will bring up provider's configuration UI.
494
495             if (newProviderSettings == null) {
496                 // Force the user into a configuration of the chosen provider
497                 if (DBG) log("Saved preferences not found - invoking config");
498                 mVMProviderSettingsForced = true;
499                 simulatePreferenceClick(mVoicemailSettings);
500             } else {
501                 if (DBG) log("Saved preferences found - switching to them");
502                 // Set this flag so if we get a failure we revert to previous provider
503                 mChangingVMorFwdDueToProviderChange = true;
504                 saveVoiceMailAndForwardingNumber(newProviderKey, newProviderSettings);
505             }
506         } else if (preference == mVoicemailNotificationVibrateWhen) {
507             mVoicemailNotificationVibrateWhen.setValue((String) objValue);
508             mVoicemailNotificationVibrateWhen.setSummary(
509                     mVoicemailNotificationVibrateWhen.getEntry());
510         } else if (preference == mButtonSipCallOptions) {
511             handleSipCallOptionsChange(objValue);
512         }
513         // always let the preference setting proceed.
514         return true;
515     }
516
517     // Preference click listener invoked on OnDialogClosed for EditPhoneNumberPreference.
518     public void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked) {
519         if (DBG) log("onPreferenceClick: request preference click on dialog close: " +
520                 buttonClicked);
521         if (buttonClicked == DialogInterface.BUTTON_NEGATIVE) {
522             return;
523         }
524         if (preference instanceof EditPhoneNumberPreference) {
525             EditPhoneNumberPreference epn = preference;
526
527             if (epn == mSubMenuVoicemailSettings) {
528                 handleVMBtnClickRequest();
529             }
530         }
531     }
532
533     /**
534      * Implemented for EditPhoneNumberPreference.GetDefaultNumberListener.
535      * This method set the default values for the various
536      * EditPhoneNumberPreference dialogs.
537      */
538     public String onGetDefaultNumber(EditPhoneNumberPreference preference) {
539         if (preference == mSubMenuVoicemailSettings) {
540             // update the voicemail number field, which takes care of the
541             // mSubMenuVoicemailSettings itself, so we should return null.
542             if (DBG) log("updating default for voicemail dialog");
543             updateVoiceNumberField();
544             return null;
545         }
546
547         String vmDisplay = mPhone.getVoiceMailNumber();
548         if (TextUtils.isEmpty(vmDisplay)) {
549             // if there is no voicemail number, we just return null to
550             // indicate no contribution.
551             return null;
552         }
553
554         // Return the voicemail number prepended with "VM: "
555         if (DBG) log("updating default for call forwarding dialogs");
556         return getString(R.string.voicemail_abbreviated) + " " + vmDisplay;
557     }
558
559
560     // override the startsubactivity call to make changes in state consistent.
561     @Override
562     public void startActivityForResult(Intent intent, int requestCode) {
563         if (requestCode == -1) {
564             // this is an intent requested from the preference framework.
565             super.startActivityForResult(intent, requestCode);
566             return;
567         }
568
569         if (DBG) log("startSubActivity: starting requested subactivity");
570         super.startActivityForResult(intent, requestCode);
571     }
572
573     private void switchToPreviousVoicemailProvider() {
574         if (DBG) log("switchToPreviousVoicemailProvider " + mPreviousVMProviderKey);
575         if (mPreviousVMProviderKey != null) {
576             if (mVMChangeCompletedSuccesfully || mFwdChangesRequireRollback) {
577                 // we have to revert with carrier
578                 showDialogIfForeground(VOICEMAIL_REVERTING_DIALOG);
579                 VoiceMailProviderSettings prevSettings =
580                     loadSettingsForVoiceMailProvider(mPreviousVMProviderKey);
581                 if (mVMChangeCompletedSuccesfully) {
582                     mNewVMNumber = prevSettings.voicemailNumber;
583                     if (DBG) log("have to revert VM to " + mNewVMNumber);
584                     mPhone.setVoiceMailNumber(
585                             mPhone.getVoiceMailAlphaTag().toString(),
586                             mNewVMNumber,
587                             Message.obtain(mRevertOptionComplete, EVENT_VOICEMAIL_CHANGED));
588                 }
589                 if (mFwdChangesRequireRollback) {
590                     if (DBG) log("have to revert fwd");
591                     final CallForwardInfo[] prevFwdSettings =
592                         prevSettings.forwardingSettings;
593                     if (prevFwdSettings != null) {
594                         Map<Integer, AsyncResult> results =
595                             mForwardingChangeResults;
596                         resetForwardingChangeState();
597                         for (int i = 0; i < prevFwdSettings.length; i++) {
598                             CallForwardInfo fi = prevFwdSettings[i];
599                             if (DBG) log("Reverting fwd #: " + i + ": " + fi.toString());
600                             // Only revert the settings for which the update
601                             // succeeded
602                             AsyncResult result = results.get(fi.reason);
603                             if (result != null && result.exception == null) {
604                                 mExpectedChangeResultReasons.add(fi.reason);
605                                 mPhone.setCallForwardingOption(
606                                         (fi.status == 1 ?
607                                                 CommandsInterface.CF_ACTION_REGISTRATION :
608                                                 CommandsInterface.CF_ACTION_DISABLE),
609                                         fi.reason,
610                                         fi.number,
611                                         fi.timeSeconds,
612                                         mRevertOptionComplete.obtainMessage(
613                                                 EVENT_FORWARDING_CHANGED, i, 0));
614                             }
615                         }
616                     }
617                 }
618             } else {
619                 if (DBG) log("No need to revert");
620                 onRevertDone();
621             }
622         }
623     }
624
625     void onRevertDone() {
626         if (DBG) log("Flipping provider key back to " + mPreviousVMProviderKey);
627         mVoicemailProviders.setValue(mPreviousVMProviderKey);
628         updateVMPreferenceWidgets(mPreviousVMProviderKey);
629         updateVoiceNumberField();
630         if (mVMOrFwdSetError != 0) {
631             showVMDialog(mVMOrFwdSetError);
632             mVMOrFwdSetError = 0;
633         }
634     }
635
636     // asynchronous result call after contacts are selected or after we return from
637     // a call to the VM settings provider.
638     @Override
639     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
640         // there are cases where the contact picker may end up sending us more than one
641         // request.  We want to ignore the request if we're not in the correct state.
642         if (requestCode ==  VOICEMAIL_PROVIDER_CFG_ID) {
643             boolean failure = false;
644
645             // No matter how the processing of result goes lets clear the flag
646             if (DBG) log("mVMProviderSettingsForced: " + mVMProviderSettingsForced);
647             final boolean isVMProviderSettingsForced = mVMProviderSettingsForced;
648             mVMProviderSettingsForced = false;
649
650             String vmNum = null;
651             if (resultCode != RESULT_OK) {
652                 if (DBG) log("onActivityResult: vm provider cfg result not OK.");
653                 failure = true;
654             } else {
655                 if (data == null) {
656                     if (DBG) log("onActivityResult: vm provider cfg result has no data");
657                     failure = true;
658                 } else {
659                     if (data.getBooleanExtra(SIGNOUT_EXTRA, false)) {
660                         if (DBG) log("Provider requested signout");
661                         if (isVMProviderSettingsForced) {
662                             if (DBG) log("Going back to previous provider on signout");
663                             switchToPreviousVoicemailProvider();
664                         } else {
665                             final String victim = getCurrentVoicemailProviderKey();
666                             if (DBG) log("Relaunching activity and ignoring " + victim);
667                             Intent i = new Intent(ACTION_ADD_VOICEMAIL);
668                             i.putExtra(IGNORE_PROVIDER_EXTRA, victim);
669                             i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
670                             this.startActivity(i);
671                         }
672                         return;
673                     }
674                     vmNum = data.getStringExtra(VM_NUMBER_EXTRA);
675                     if (vmNum == null || vmNum.length() == 0) {
676                         if (DBG) log("onActivityResult: vm provider cfg result has no vmnum");
677                         failure = true;
678                     }
679                 }
680             }
681             if (failure) {
682                 if (DBG) log("Failure in return from voicemail provider");
683                 if (isVMProviderSettingsForced) {
684                     switchToPreviousVoicemailProvider();
685                 } else {
686                     if (DBG) log("Not switching back the provider since this is not forced config");
687                 }
688                 return;
689             }
690             mChangingVMorFwdDueToProviderChange = isVMProviderSettingsForced;
691             final String fwdNum = data.getStringExtra(FWD_NUMBER_EXTRA);
692
693             // TODO(iliat): It would be nice to load the current network setting for this and
694             // send it to the provider when it's config is invoked so it can use this as default
695             final int fwdNumTime = data.getIntExtra(FWD_NUMBER_TIME_EXTRA, 20);
696
697             if (DBG) log("onActivityResult: vm provider cfg result " +
698                     (fwdNum != null ? "has" : " does not have") + " forwarding number");
699             saveVoiceMailAndForwardingNumber(getCurrentVoicemailProviderKey(),
700                     new VoiceMailProviderSettings(vmNum, fwdNum, fwdNumTime));
701             return;
702         }
703
704         if (resultCode != RESULT_OK) {
705             if (DBG) log("onActivityResult: contact picker result not OK.");
706             return;
707         }
708
709         Cursor cursor = getContentResolver().query(data.getData(),
710                 NUM_PROJECTION, null, null, null);
711         if ((cursor == null) || (!cursor.moveToFirst())) {
712             if (DBG) log("onActivityResult: bad contact data, no results found.");
713             return;
714         }
715
716         switch (requestCode) {
717             case VOICEMAIL_PREF_ID:
718                 mSubMenuVoicemailSettings.onPickActivityResult(cursor.getString(0));
719                 break;
720             default:
721                 // TODO: may need exception here.
722         }
723     }
724
725     // Voicemail button logic
726     private void handleVMBtnClickRequest() {
727         // normally called on the dialog close.
728
729         // Since we're stripping the formatting out on the getPhoneNumber()
730         // call now, we won't need to do so here anymore.
731
732         saveVoiceMailAndForwardingNumber(
733                 getCurrentVoicemailProviderKey(),
734                 new VoiceMailProviderSettings(mSubMenuVoicemailSettings.getPhoneNumber(),
735                         FWD_SETTINGS_DONT_TOUCH));
736     }
737
738
739     /**
740      * Wrapper around showDialog() that will silently do nothing if we're
741      * not in the foreground.
742      *
743      * This is useful here because most of the dialogs we display from
744      * this class are triggered by asynchronous events (like
745      * success/failure messages from the telephony layer) and it's
746      * possible for those events to come in even after the user has gone
747      * to a different screen.
748      */
749     // TODO: this is too brittle: it's still easy to accidentally add new
750     // code here that calls showDialog() directly (which will result in a
751     // WindowManager$BadTokenException if called after the activity has
752     // been stopped.)
753     //
754     // It would be cleaner to do the "if (mForeground)" check in one
755     // central place, maybe by using a single Handler for all asynchronous
756     // events (and have *that* discard events if we're not in the
757     // foreground.)
758     //
759     // Unfortunately it's not that simple, since we sometimes need to do
760     // actual work to handle these events whether or not we're in the
761     // foreground (see the Handler code in mSetOptionComplete for
762     // example.)
763     private void showDialogIfForeground(int id) {
764         if (mForeground) {
765             showDialog(id);
766         }
767     }
768
769     private void dismissDialogSafely(int id) {
770         try {
771             dismissDialog(id);
772         } catch (IllegalArgumentException e) {
773             // This is expected in the case where we were in the background
774             // at the time we would normally have shown the dialog, so we didn't
775             // show it.
776         }
777     }
778
779     private void saveVoiceMailAndForwardingNumber(String key,
780             VoiceMailProviderSettings newSettings) {
781         if (DBG) log("saveVoiceMailAndForwardingNumber: " + newSettings.toString());
782         mNewVMNumber = newSettings.voicemailNumber;
783         // empty vm number == clearing the vm number ?
784         if (mNewVMNumber == null) {
785             mNewVMNumber = "";
786         }
787
788         mNewFwdSettings = newSettings.forwardingSettings;
789         if (DBG) log("newFwdNumber " +
790                 String.valueOf((mNewFwdSettings != null ? mNewFwdSettings.length : 0))
791                 + " settings");
792
793         // No fwd settings on CDMA
794         if (mPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
795             if (DBG) log("ignoring forwarding setting since this is CDMA phone");
796             mNewFwdSettings = FWD_SETTINGS_DONT_TOUCH;
797         }
798
799         //throw a warning if the vm is the same and we do not touch forwarding.
800         if (mNewVMNumber.equals(mOldVmNumber) && mNewFwdSettings == FWD_SETTINGS_DONT_TOUCH) {
801             showVMDialog(MSG_VM_NOCHANGE);
802             return;
803         }
804
805         maybeSaveSettingsForVoicemailProvider(key, newSettings);
806         mVMChangeCompletedSuccesfully = false;
807         mFwdChangesRequireRollback = false;
808         mVMOrFwdSetError = 0;
809         if (!key.equals(mPreviousVMProviderKey)) {
810             mReadingSettingsForDefaultProvider =
811                 mPreviousVMProviderKey.equals(DEFAULT_VM_PROVIDER_KEY);
812             if (DBG) log("Reading current forwarding settings");
813             mForwardingReadResults = new CallForwardInfo[FORWARDING_SETTINGS_REASONS.length];
814             for (int i = 0; i < FORWARDING_SETTINGS_REASONS.length; i++) {
815                 mForwardingReadResults[i] = null;
816                 mPhone.getCallForwardingOption(FORWARDING_SETTINGS_REASONS[i],
817                         mGetOptionComplete.obtainMessage(EVENT_FORWARDING_GET_COMPLETED, i, 0));
818             }
819             showDialogIfForeground(VOICEMAIL_FWD_READING_DIALOG);
820         } else {
821             saveVoiceMailAndForwardingNumberStage2();
822         }
823     }
824
825     private final Handler mGetOptionComplete = new Handler() {
826         @Override
827         public void handleMessage(Message msg) {
828             AsyncResult result = (AsyncResult) msg.obj;
829             switch (msg.what) {
830                 case EVENT_FORWARDING_GET_COMPLETED:
831                     handleForwardingSettingsReadResult(result, msg.arg1);
832                     break;
833             }
834         }
835     };
836
837     void handleForwardingSettingsReadResult(AsyncResult ar, int idx) {
838         if (DBG) Log.d(LOG_TAG, "handleForwardingSettingsReadResult: " + idx);
839         Throwable error = null;
840         if (ar.exception != null) {
841             if (DBG) Log.d(LOG_TAG, "FwdRead: ar.exception=" +
842                     ar.exception.getMessage());
843             error = ar.exception;
844         }
845         if (ar.userObj instanceof Throwable) {
846             if (DBG) Log.d(LOG_TAG, "FwdRead: userObj=" +
847                     ((Throwable)ar.userObj).getMessage());
848             error = (Throwable)ar.userObj;
849         }
850
851         // We may have already gotten an error and decided to ignore the other results.
852         if (mForwardingReadResults == null) {
853             if (DBG) Log.d(LOG_TAG, "ignoring fwd reading result: " + idx);
854             return;
855         }
856
857         // In case of error ignore other results, show an error dialog
858         if (error != null) {
859             if (DBG) Log.d(LOG_TAG, "Error discovered for fwd read : " + idx);
860             mForwardingReadResults = null;
861             dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG);
862             showVMDialog(MSG_FW_GET_EXCEPTION);
863             return;
864         }
865
866         // Get the forwarding info
867         final CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
868         CallForwardInfo fi = null;
869         for (int i = 0 ; i < cfInfoArray.length; i++) {
870             if ((cfInfoArray[i].serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) {
871                 fi = cfInfoArray[i];
872                 break;
873             }
874         }
875         if (fi == null) {
876
877             // In case we go nothing it means we need this reason disabled
878             // so create a CallForwardInfo for capturing this
879             if (DBG) Log.d(LOG_TAG, "Creating default info for " + idx);
880             fi = new CallForwardInfo();
881             fi.status = 0;
882             fi.reason = FORWARDING_SETTINGS_REASONS[idx];
883             fi.serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
884         } else {
885             // if there is not a forwarding number, ensure the entry is set to "not active."
886             if (fi.number == null || fi.number.length() == 0) {
887                 fi.status = 0;
888             }
889
890             if (DBG) Log.d(LOG_TAG, "Got  " + fi.toString() + " for " + idx);
891         }
892         mForwardingReadResults[idx] = fi;
893
894         // Check if we got all the results already
895         boolean done = true;
896         for (int i = 0; i < mForwardingReadResults.length; i++) {
897             if (mForwardingReadResults[i] == null) {
898                 done = false;
899                 break;
900             }
901         }
902         if (done) {
903             if (DBG) Log.d(LOG_TAG, "Done receiving fwd info");
904             dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG);
905             if (mReadingSettingsForDefaultProvider) {
906                 maybeSaveSettingsForVoicemailProvider(DEFAULT_VM_PROVIDER_KEY,
907                         new VoiceMailProviderSettings(this.mOldVmNumber,
908                                 mForwardingReadResults));
909                 mReadingSettingsForDefaultProvider = false;
910             }
911             saveVoiceMailAndForwardingNumberStage2();
912         } else {
913             if (DBG) Log.d(LOG_TAG, "Not done receiving fwd info");
914         }
915     }
916
917     private CallForwardInfo infoForReason(CallForwardInfo[] infos, int reason) {
918         CallForwardInfo result = null;
919         if (null != infos) {
920             for (CallForwardInfo info : infos) {
921                 if (info.reason == reason) {
922                     result = info;
923                     break;
924                 }
925             }
926         }
927         return result;
928     }
929
930     private boolean isUpdateRequired(CallForwardInfo oldInfo,
931             CallForwardInfo newInfo) {
932         boolean result = true;
933         if (0 == newInfo.status) {
934             // If we're disabling a type of forwarding, and it's already
935             // disabled for the account, don't make any change
936             if (oldInfo != null && oldInfo.status == 0) {
937                 result = false;
938             }
939         }
940         return result;
941     }
942
943     private void resetForwardingChangeState() {
944         mForwardingChangeResults = new HashMap<Integer, AsyncResult>();
945         mExpectedChangeResultReasons = new HashSet<Integer>();
946     }
947
948     // Called after we are done saving the previous forwarding settings if
949     // we needed.
950     private void saveVoiceMailAndForwardingNumberStage2() {
951         mForwardingChangeResults = null;
952         mVoicemailChangeResult = null;
953         if (mNewFwdSettings != FWD_SETTINGS_DONT_TOUCH) {
954             resetForwardingChangeState();
955             for (int i = 0; i < mNewFwdSettings.length; i++) {
956                 CallForwardInfo fi = mNewFwdSettings[i];
957
958                 final boolean doUpdate = isUpdateRequired(infoForReason(
959                             mForwardingReadResults, fi.reason), fi);
960
961                 if (doUpdate) {
962                     if (DBG) log("Setting fwd #: " + i + ": " + fi.toString());
963                     mExpectedChangeResultReasons.add(i);
964
965                     mPhone.setCallForwardingOption(
966                             fi.status == 1 ?
967                                     CommandsInterface.CF_ACTION_REGISTRATION :
968                                     CommandsInterface.CF_ACTION_DISABLE,
969                             fi.reason,
970                             fi.number,
971                             fi.timeSeconds,
972                             mSetOptionComplete.obtainMessage(
973                                     EVENT_FORWARDING_CHANGED, fi.reason, 0));
974                 }
975              }
976              showDialogIfForeground(VOICEMAIL_FWD_SAVING_DIALOG);
977         } else {
978             if (DBG) log("Not touching fwd #");
979             setVMNumberWithCarrier();
980         }
981     }
982
983     void setVMNumberWithCarrier() {
984         if (DBG) log("save voicemail #: " + mNewVMNumber);
985         mPhone.setVoiceMailNumber(
986                 mPhone.getVoiceMailAlphaTag().toString(),
987                 mNewVMNumber,
988                 Message.obtain(mSetOptionComplete, EVENT_VOICEMAIL_CHANGED));
989     }
990
991     /**
992      * Callback to handle option update completions
993      */
994     private final Handler mSetOptionComplete = new Handler() {
995         @Override
996         public void handleMessage(Message msg) {
997             AsyncResult result = (AsyncResult) msg.obj;
998             boolean done = false;
999             switch (msg.what) {
1000                 case EVENT_VOICEMAIL_CHANGED:
1001                     mVoicemailChangeResult = result;
1002                     mVMChangeCompletedSuccesfully = checkVMChangeSuccess() == null;
1003                     if (DBG) log("VM change complete msg, VM change done = " +
1004                             String.valueOf(mVMChangeCompletedSuccesfully));
1005                     done = true;
1006                     break;
1007                 case EVENT_FORWARDING_CHANGED:
1008                     mForwardingChangeResults.put(msg.arg1, result);
1009                     if (result.exception != null) {
1010                         if (DBG) log("Error in setting fwd# " + msg.arg1 + ": " +
1011                                 result.exception.getMessage());
1012                     } else {
1013                         if (DBG) log("Success in setting fwd# " + msg.arg1);
1014                     }
1015                     final boolean completed = checkForwardingCompleted();
1016                     if (completed) {
1017                         if (checkFwdChangeSuccess() == null) {
1018                             if (DBG) log("Overall fwd changes completed ok, starting vm change");
1019                             setVMNumberWithCarrier();
1020                         } else {
1021                             if (DBG) log("Overall fwd changes completed, failure");
1022                             mFwdChangesRequireRollback = false;
1023                             Iterator<Map.Entry<Integer,AsyncResult>> it =
1024                                 mForwardingChangeResults.entrySet().iterator();
1025                             while (it.hasNext()) {
1026                                 Map.Entry<Integer,AsyncResult> entry = it.next();
1027                                 if (entry.getValue().exception == null) {
1028                                     // If at least one succeeded we have to revert
1029                                     if (DBG) log("Rollback will be required");
1030                                     mFwdChangesRequireRollback =true;
1031                                     break;
1032                                 }
1033                             }
1034                             done = true;
1035                         }
1036                     }
1037                     break;
1038                 default:
1039                     // TODO: should never reach this, may want to throw exception
1040             }
1041             if (done) {
1042                 if (DBG) log("All VM provider related changes done");
1043                 if (mForwardingChangeResults != null) {
1044                     dismissDialogSafely(VOICEMAIL_FWD_SAVING_DIALOG);
1045                 }
1046                 handleSetVMOrFwdMessage();
1047             }
1048         }
1049     };
1050
1051     /**
1052      * Callback to handle option revert completions
1053      */
1054     private final Handler mRevertOptionComplete = new Handler() {
1055         @Override
1056         public void handleMessage(Message msg) {
1057             AsyncResult result = (AsyncResult) msg.obj;
1058             switch (msg.what) {
1059                 case EVENT_VOICEMAIL_CHANGED:
1060                     mVoicemailChangeResult = result;
1061                     if (DBG) log("VM revert complete msg");
1062                     break;
1063                 case EVENT_FORWARDING_CHANGED:
1064                     mForwardingChangeResults.put(msg.arg1, result);
1065                     if (result.exception != null) {
1066                         if (DBG) log("Error in reverting fwd# " + msg.arg1 + ": " +
1067                                 result.exception.getMessage());
1068                     } else {
1069                         if (DBG) log("Success in reverting fwd# " + msg.arg1);
1070                     }
1071                     if (DBG) log("FWD revert complete msg ");
1072                     break;
1073                 default:
1074                     // TODO: should never reach this, may want to throw exception
1075             }
1076             final boolean done =
1077                 (!mVMChangeCompletedSuccesfully || mVoicemailChangeResult != null) &&
1078                 (!mFwdChangesRequireRollback || checkForwardingCompleted());
1079             if (done) {
1080                 if (DBG) log("All VM reverts done");
1081                 dismissDialogSafely(VOICEMAIL_REVERTING_DIALOG);
1082                 onRevertDone();
1083             }
1084         }
1085     };
1086
1087     /**
1088      * @return true if forwarding change has completed
1089      */
1090     private boolean checkForwardingCompleted() {
1091         boolean result;
1092         if (mForwardingChangeResults == null) {
1093             result = true;
1094         } else {
1095             // return true iff there is a change result for every reason for
1096             // which we expected a result
1097             result = true;
1098             for (Integer reason : mExpectedChangeResultReasons) {
1099                 if (mForwardingChangeResults.get(reason) == null) {
1100                     result = false;
1101                     break;
1102                 }
1103             }
1104         }
1105         return result;
1106     }
1107     /**
1108      * @return error string or null if successful
1109      */
1110     private String checkFwdChangeSuccess() {
1111         String result = null;
1112         Iterator<Map.Entry<Integer,AsyncResult>> it =
1113             mForwardingChangeResults.entrySet().iterator();
1114         while (it.hasNext()) {
1115             Map.Entry<Integer,AsyncResult> entry = it.next();
1116             Throwable exception = entry.getValue().exception;
1117             if (exception != null) {
1118                 result = exception.getMessage();
1119                 if (result == null) {
1120                     result = "";
1121                 }
1122                 break;
1123             }
1124         }
1125         return result;
1126     }
1127
1128     /**
1129      * @return error string or null if successful
1130      */
1131     private String checkVMChangeSuccess() {
1132         if (mVoicemailChangeResult.exception != null) {
1133             final String msg = mVoicemailChangeResult.exception.getMessage();
1134             if (msg == null) {
1135                 return "";
1136             }
1137             return msg;
1138         }
1139         return null;
1140     }
1141
1142     private void handleSetVMOrFwdMessage() {
1143         if (DBG) {
1144             log("handleSetVMMessage: set VM request complete");
1145         }
1146         boolean success = true;
1147         boolean fwdFailure = false;
1148         String exceptionMessage = "";
1149         if (mForwardingChangeResults != null) {
1150             exceptionMessage = checkFwdChangeSuccess();
1151             if (exceptionMessage != null) {
1152                 success = false;
1153                 fwdFailure = true;
1154             }
1155         }
1156         if (success) {
1157             exceptionMessage = checkVMChangeSuccess();
1158             if (exceptionMessage != null) {
1159                 success = false;
1160             }
1161         }
1162         if (success) {
1163             if (DBG) log("change VM success!");
1164             handleVMAndFwdSetSuccess(MSG_VM_OK);
1165             updateVoiceNumberField();
1166         } else {
1167             if (fwdFailure) {
1168                 log("change FW failed: " + exceptionMessage);
1169                 handleVMOrFwdSetError(MSG_FW_SET_EXCEPTION);
1170             } else {
1171                 log("change VM failed: " + exceptionMessage);
1172                 handleVMOrFwdSetError(MSG_VM_EXCEPTION);
1173             }
1174         }
1175     }
1176
1177     private void handleVMOrFwdSetError(int msgId) {
1178         if (mChangingVMorFwdDueToProviderChange) {
1179             mVMOrFwdSetError = msgId;
1180             mChangingVMorFwdDueToProviderChange = false;
1181             switchToPreviousVoicemailProvider();
1182             return;
1183         }
1184         mChangingVMorFwdDueToProviderChange = false;
1185         showVMDialog(msgId);
1186         updateVoiceNumberField();
1187     }
1188
1189     private void handleVMAndFwdSetSuccess(int msgId) {
1190         mChangingVMorFwdDueToProviderChange = false;
1191         showVMDialog(msgId);
1192     }
1193
1194     /*
1195      * Methods used to sync UI state with that of the network
1196      */
1197     // update the voicemail number from what we've recorded on the sim.
1198     private void updateVoiceNumberField() {
1199         if (mSubMenuVoicemailSettings == null) {
1200             return;
1201         }
1202
1203         mOldVmNumber = mPhone.getVoiceMailNumber();
1204         if (mOldVmNumber == null) {
1205             mOldVmNumber = "";
1206         }
1207         mSubMenuVoicemailSettings.setPhoneNumber(mOldVmNumber);
1208         final String summary = (mOldVmNumber.length() > 0) ? mOldVmNumber :
1209             getString(R.string.voicemail_number_not_set);
1210         mSubMenuVoicemailSettings.setSummary(summary);
1211     }
1212
1213     /*
1214      * Helper Methods for Activity class.
1215      * The initial query commands are split into two pieces now
1216      * for individual expansion.  This combined with the ability
1217      * to cancel queries allows for a much better user experience,
1218      * and also ensures that the user only waits to update the
1219      * data that is relevant.
1220      */
1221
1222     @Override
1223     protected void onPrepareDialog(int id, Dialog dialog) {
1224         super.onPrepareDialog(id, dialog);
1225         mCurrentDialogId = id;
1226     }
1227
1228     // dialog creation method, called by showDialog()
1229     @Override
1230     protected Dialog onCreateDialog(int id) {
1231         if ((id == VM_RESPONSE_ERROR) || (id == VM_NOCHANGE_ERROR) ||
1232             (id == FW_SET_RESPONSE_ERROR) || (id == FW_GET_RESPONSE_ERROR) ||
1233                 (id == VOICEMAIL_DIALOG_CONFIRM)) {
1234
1235             AlertDialog.Builder b = new AlertDialog.Builder(this);
1236
1237             int msgId;
1238             int titleId = R.string.error_updating_title;
1239             switch (id) {
1240                 case VOICEMAIL_DIALOG_CONFIRM:
1241                     msgId = R.string.vm_changed;
1242                     titleId = R.string.voicemail;
1243                     // Set Button 2
1244                     b.setNegativeButton(R.string.close_dialog, this);
1245                     break;
1246                 case VM_NOCHANGE_ERROR:
1247                     // even though this is technically an error,
1248                     // keep the title friendly.
1249                     msgId = R.string.no_change;
1250                     titleId = R.string.voicemail;
1251                     // Set Button 2
1252                     b.setNegativeButton(R.string.close_dialog, this);
1253                     break;
1254                 case VM_RESPONSE_ERROR:
1255                     msgId = R.string.vm_change_failed;
1256                     // Set Button 1
1257                     b.setPositiveButton(R.string.close_dialog, this);
1258                     break;
1259                 case FW_SET_RESPONSE_ERROR:
1260                     msgId = R.string.fw_change_failed;
1261                     // Set Button 1
1262                     b.setPositiveButton(R.string.close_dialog, this);
1263                     break;
1264                 case FW_GET_RESPONSE_ERROR:
1265                     msgId = R.string.fw_get_in_vm_failed;
1266                     b.setPositiveButton(R.string.alert_dialog_yes, this);
1267                     b.setNegativeButton(R.string.alert_dialog_no, this);
1268                     break;
1269                 default:
1270                     msgId = R.string.exception_error;
1271                     // Set Button 3, tells the activity that the error is
1272                     // not recoverable on dialog exit.
1273                     b.setNeutralButton(R.string.close_dialog, this);
1274                     break;
1275             }
1276
1277             b.setTitle(getText(titleId));
1278             String message = getText(msgId).toString();
1279             b.setMessage(message);
1280             b.setCancelable(false);
1281             AlertDialog dialog = b.create();
1282
1283             // make the dialog more obvious by bluring the background.
1284             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
1285
1286             return dialog;
1287         } else if (id == VOICEMAIL_FWD_SAVING_DIALOG || id == VOICEMAIL_FWD_READING_DIALOG ||
1288                 id == VOICEMAIL_REVERTING_DIALOG) {
1289             ProgressDialog dialog = new ProgressDialog(this);
1290             dialog.setTitle(getText(R.string.updating_title));
1291             dialog.setIndeterminate(true);
1292             dialog.setCancelable(false);
1293             dialog.setMessage(getText(
1294                     id == VOICEMAIL_FWD_SAVING_DIALOG ? R.string.updating_settings :
1295                     (id == VOICEMAIL_REVERTING_DIALOG ? R.string.reverting_settings :
1296                     R.string.reading_settings)));
1297             return dialog;
1298         }
1299
1300
1301         return null;
1302     }
1303
1304     // This is a method implemented for DialogInterface.OnClickListener.
1305     // Used with the error dialog to close the app, voicemail dialog to just dismiss.
1306     // Close button is mapped to BUTTON_POSITIVE for the errors that close the activity,
1307     // while those that are mapped to BUTTON_NEUTRAL only move the preference focus.
1308     public void onClick(DialogInterface dialog, int which) {
1309         dialog.dismiss();
1310         switch (which){
1311             case DialogInterface.BUTTON_NEUTRAL:
1312                 if (DBG) log("Neutral button");
1313                 break;
1314             case DialogInterface.BUTTON_NEGATIVE:
1315                 if (DBG) log("Negative button");
1316                 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) {
1317                     // We failed to get current forwarding settings and the user
1318                     // does not wish to continue.
1319                     switchToPreviousVoicemailProvider();
1320                 }
1321                 break;
1322             case DialogInterface.BUTTON_POSITIVE:
1323                 if (DBG) log("Positive button");
1324                 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) {
1325                     // We failed to get current forwarding settings but the user
1326                     // wishes to continue changing settings to the new vm provider
1327                     saveVoiceMailAndForwardingNumberStage2();
1328                 } else {
1329                     finish();
1330                 }
1331                 return;
1332             default:
1333                 // just let the dialog close and go back to the input
1334         }
1335         // In all dialogs, all buttons except BUTTON_POSITIVE lead to the end of user interaction
1336         // with settings UI. If we were called to explicitly configure voice mail then
1337         // we finish the settings activity here to come back to whatever the user was doing.
1338         if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) {
1339             finish();
1340         }
1341     }
1342
1343     // set the app state with optional status.
1344     private void showVMDialog(int msgStatus) {
1345         switch (msgStatus) {
1346             // It's a bit worrisome to punt in the error cases here when we're
1347             // not in the foreground; maybe toast instead?
1348             case MSG_VM_EXCEPTION:
1349                 showDialogIfForeground(VM_RESPONSE_ERROR);
1350                 break;
1351             case MSG_FW_SET_EXCEPTION:
1352                 showDialogIfForeground(FW_SET_RESPONSE_ERROR);
1353                 break;
1354             case MSG_FW_GET_EXCEPTION:
1355                 showDialogIfForeground(FW_GET_RESPONSE_ERROR);
1356                 break;
1357             case MSG_VM_NOCHANGE:
1358                 showDialogIfForeground(VM_NOCHANGE_ERROR);
1359                 break;
1360             case MSG_VM_OK:
1361                 showDialogIfForeground(VOICEMAIL_DIALOG_CONFIRM);
1362                 break;
1363             case MSG_OK:
1364             default:
1365                 // This should never happen.
1366         }
1367     }
1368
1369     /*
1370      * Activity class methods
1371      */
1372
1373     @Override
1374     protected void onCreate(Bundle icicle) {
1375         super.onCreate(icicle);
1376         if (DBG) log("Creating activity");
1377         mPhone = PhoneApp.getPhone();
1378
1379         addPreferencesFromResource(R.xml.call_feature_setting);
1380
1381         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
1382
1383         // get buttons
1384         PreferenceScreen prefSet = getPreferenceScreen();
1385         mSubMenuVoicemailSettings = (EditPhoneNumberPreference)findPreference(BUTTON_VOICEMAIL_KEY);
1386         if (mSubMenuVoicemailSettings != null) {
1387             mSubMenuVoicemailSettings.setParentActivity(this, VOICEMAIL_PREF_ID, this);
1388             mSubMenuVoicemailSettings.setDialogOnClosedListener(this);
1389             mSubMenuVoicemailSettings.setDialogTitle(R.string.voicemail_settings_number_label);
1390         }
1391
1392         mButtonDTMF = (ListPreference) findPreference(BUTTON_DTMF_KEY);
1393         mButtonAutoRetry = (CheckBoxPreference) findPreference(BUTTON_RETRY_KEY);
1394         mButtonHAC = (CheckBoxPreference) findPreference(BUTTON_HAC_KEY);
1395         mButtonTTY = (ListPreference) findPreference(BUTTON_TTY_KEY);
1396         mVoicemailProviders = (ListPreference) findPreference(BUTTON_VOICEMAIL_PROVIDER_KEY);
1397         if (mVoicemailProviders != null) {
1398             mVoicemailProviders.setOnPreferenceChangeListener(this);
1399             mVoicemailSettings = (PreferenceScreen)findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
1400             mVoicemailNotificationVibrateWhen =
1401                     (ListPreference) findPreference(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY);
1402             mVoicemailNotificationVibrateWhen.setOnPreferenceChangeListener(this);
1403
1404             initVoiceMailProviders();
1405         }
1406
1407         if (mButtonDTMF != null) {
1408             if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
1409                 mButtonDTMF.setOnPreferenceChangeListener(this);
1410             } else {
1411                 prefSet.removePreference(mButtonDTMF);
1412                 mButtonDTMF = null;
1413             }
1414         }
1415
1416         if (mButtonAutoRetry != null) {
1417             if (getResources().getBoolean(R.bool.auto_retry_enabled)) {
1418                 mButtonAutoRetry.setOnPreferenceChangeListener(this);
1419             } else {
1420                 prefSet.removePreference(mButtonAutoRetry);
1421                 mButtonAutoRetry = null;
1422             }
1423         }
1424
1425         if (mButtonHAC != null) {
1426             if (getResources().getBoolean(R.bool.hac_enabled)) {
1427
1428                 mButtonHAC.setOnPreferenceChangeListener(this);
1429             } else {
1430                 prefSet.removePreference(mButtonHAC);
1431                 mButtonHAC = null;
1432             }
1433         }
1434
1435         if (mButtonTTY != null) {
1436             if (getResources().getBoolean(R.bool.tty_enabled)) {
1437                 mButtonTTY.setOnPreferenceChangeListener(this);
1438             } else {
1439                 prefSet.removePreference(mButtonTTY);
1440                 mButtonTTY = null;
1441             }
1442         }
1443
1444         if (!getResources().getBoolean(R.bool.world_phone)) {
1445             Preference options = prefSet.findPreference(BUTTON_CDMA_OPTIONS);
1446             if (options != null)
1447                 prefSet.removePreference(options);
1448             options = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS);
1449             if (options != null)
1450                 prefSet.removePreference(options);
1451
1452             int phoneType = mPhone.getPhoneType();
1453             if (phoneType == Phone.PHONE_TYPE_CDMA) {
1454                 Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
1455                 if (fdnButton != null)
1456                     prefSet.removePreference(fdnButton);
1457                 if (!getResources().getBoolean(R.bool.config_voice_privacy_disable)) {
1458                     addPreferencesFromResource(R.xml.cdma_call_privacy);
1459                 }
1460             } else if (phoneType == Phone.PHONE_TYPE_GSM) {
1461                 addPreferencesFromResource(R.xml.gsm_umts_call_options);
1462             } else {
1463                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
1464             }
1465         }
1466
1467         // create intent to bring up contact list
1468         mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT);
1469         mContactListIntent.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
1470
1471         // check the intent that started this activity and pop up the voicemail
1472         // dialog if we've been asked to.
1473         // If we have at least one non default VM provider registered then bring up
1474         // the selection for the VM provider, otherwise bring up a VM number dialog.
1475         // We only bring up the dialog the first time we are called (not after orientation change)
1476         if (icicle == null) {
1477             if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL) &&
1478                     mVoicemailProviders != null) {
1479                 if (mVMProvidersData.size() > 1) {
1480                     simulatePreferenceClick(mVoicemailProviders);
1481                 } else {
1482                     onPreferenceChange(mVoicemailProviders, DEFAULT_VM_PROVIDER_KEY);
1483                     mVoicemailProviders.setValue(DEFAULT_VM_PROVIDER_KEY);
1484                 }
1485             }
1486         }
1487         updateVoiceNumberField();
1488         mVMProviderSettingsForced = false;
1489         createSipCallSettings();
1490
1491         ActionBar actionBar = getActionBar();
1492         if (actionBar != null) {
1493             // android.R.id.home will be triggered in onOptionsItemSelected()
1494             actionBar.setDisplayHomeAsUpEnabled(true);
1495         }
1496     }
1497
1498     private void createSipCallSettings() {
1499         // Add Internet call settings.
1500         if (PhoneUtils.isVoipSupported()) {
1501             mSipManager = SipManager.newInstance(this);
1502             mSipSharedPreferences = new SipSharedPreferences(this);
1503             addPreferencesFromResource(R.xml.sip_settings_category);
1504             mButtonSipCallOptions = getSipCallOptionPreference();
1505             mButtonSipCallOptions.setOnPreferenceChangeListener(this);
1506             mButtonSipCallOptions.setValueIndex(
1507                     mButtonSipCallOptions.findIndexOfValue(
1508                             mSipSharedPreferences.getSipCallOption()));
1509             mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry());
1510         }
1511     }
1512
1513     // Gets the call options for SIP depending on whether SIP is allowed only
1514     // on Wi-Fi only; also make the other options preference invisible.
1515     private ListPreference getSipCallOptionPreference() {
1516         ListPreference wifiAnd3G = (ListPreference)
1517                 findPreference(BUTTON_SIP_CALL_OPTIONS);
1518         ListPreference wifiOnly = (ListPreference)
1519                 findPreference(BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY);
1520         PreferenceGroup sipSettings = (PreferenceGroup)
1521                 findPreference(SIP_SETTINGS_CATEGORY_KEY);
1522         if (SipManager.isSipWifiOnly(this)) {
1523             sipSettings.removePreference(wifiAnd3G);
1524             return wifiOnly;
1525         } else {
1526             sipSettings.removePreference(wifiOnly);
1527             return wifiAnd3G;
1528         }
1529     }
1530
1531     @Override
1532     protected void onResume() {
1533         super.onResume();
1534         mForeground = true;
1535
1536         if (isAirplaneModeOn()) {
1537             Preference sipSettings = findPreference(SIP_SETTINGS_CATEGORY_KEY);
1538             PreferenceScreen screen = getPreferenceScreen();
1539             int count = screen.getPreferenceCount();
1540             for (int i = 0 ; i < count ; ++i) {
1541                 Preference pref = screen.getPreference(i);
1542                 if (pref != sipSettings) pref.setEnabled(false);
1543             }
1544             return;
1545         }
1546
1547         if (mButtonDTMF != null) {
1548             int dtmf = Settings.System.getInt(getContentResolver(),
1549                     Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_NORMAL);
1550             mButtonDTMF.setValueIndex(dtmf);
1551         }
1552
1553         if (mButtonAutoRetry != null) {
1554             int autoretry = Settings.System.getInt(getContentResolver(),
1555                     Settings.System.CALL_AUTO_RETRY, 0);
1556             mButtonAutoRetry.setChecked(autoretry != 0);
1557         }
1558
1559         if (mButtonHAC != null) {
1560             int hac = Settings.System.getInt(getContentResolver(), Settings.System.HEARING_AID, 0);
1561             mButtonHAC.setChecked(hac != 0);
1562         }
1563
1564         if (mButtonTTY != null) {
1565             int settingsTtyMode = Settings.Secure.getInt(getContentResolver(),
1566                     Settings.Secure.PREFERRED_TTY_MODE,
1567                     Phone.TTY_MODE_OFF);
1568             mButtonTTY.setValue(Integer.toString(settingsTtyMode));
1569             updatePreferredTtyModeSummary(settingsTtyMode);
1570         }
1571     }
1572
1573     private boolean isAirplaneModeOn() {
1574         return Settings.System.getInt(getContentResolver(),
1575                 Settings.System.AIRPLANE_MODE_ON, 0) != 0;
1576     }
1577
1578     private void handleTTYChange(Preference preference, Object objValue) {
1579         int buttonTtyMode;
1580         buttonTtyMode = Integer.valueOf((String) objValue).intValue();
1581         int settingsTtyMode = android.provider.Settings.Secure.getInt(
1582                 getContentResolver(),
1583                 android.provider.Settings.Secure.PREFERRED_TTY_MODE, preferredTtyMode);
1584         if (DBG) log("handleTTYChange: requesting set TTY mode enable (TTY) to" +
1585                 Integer.toString(buttonTtyMode));
1586
1587         if (buttonTtyMode != settingsTtyMode) {
1588             switch(buttonTtyMode) {
1589             case Phone.TTY_MODE_OFF:
1590             case Phone.TTY_MODE_FULL:
1591             case Phone.TTY_MODE_HCO:
1592             case Phone.TTY_MODE_VCO:
1593                 android.provider.Settings.Secure.putInt(getContentResolver(),
1594                         android.provider.Settings.Secure.PREFERRED_TTY_MODE, buttonTtyMode);
1595                 break;
1596             default:
1597                 buttonTtyMode = Phone.TTY_MODE_OFF;
1598             }
1599
1600             mButtonTTY.setValue(Integer.toString(buttonTtyMode));
1601             updatePreferredTtyModeSummary(buttonTtyMode);
1602             Intent ttyModeChanged = new Intent(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);
1603             ttyModeChanged.putExtra(TtyIntent.TTY_PREFFERED_MODE, buttonTtyMode);
1604             sendBroadcast(ttyModeChanged);
1605         }
1606     }
1607
1608     private void handleSipCallOptionsChange(Object objValue) {
1609         String option = objValue.toString();
1610         mSipSharedPreferences.setSipCallOption(option);
1611         mButtonSipCallOptions.setValueIndex(
1612                 mButtonSipCallOptions.findIndexOfValue(option));
1613         mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry());
1614     }
1615
1616     private void updatePreferredTtyModeSummary(int TtyMode) {
1617         String [] txts = getResources().getStringArray(R.array.tty_mode_entries);
1618         switch(TtyMode) {
1619             case Phone.TTY_MODE_OFF:
1620             case Phone.TTY_MODE_HCO:
1621             case Phone.TTY_MODE_VCO:
1622             case Phone.TTY_MODE_FULL:
1623                 mButtonTTY.setSummary(txts[TtyMode]);
1624                 break;
1625             default:
1626                 mButtonTTY.setEnabled(false);
1627                 mButtonTTY.setSummary(txts[Phone.TTY_MODE_OFF]);
1628         }
1629     }
1630
1631     private static void log(String msg) {
1632         Log.d(LOG_TAG, msg);
1633     }
1634
1635     /**
1636      * Updates the look of the VM preference widgets based on current VM provider settings.
1637      * Note that the provider name is loaded form the found activity via loadLabel in
1638      * initVoiceMailProviders in order for it to be localizable.
1639      */
1640     private void updateVMPreferenceWidgets(String currentProviderSetting) {
1641         final String key = currentProviderSetting;
1642         final VoiceMailProvider provider = mVMProvidersData.get(key);
1643
1644         /* This is the case when we are coming up on a freshly wiped phone and there is no
1645          persisted value for the list preference mVoicemailProviders.
1646          In this case we want to show the UI asking the user to select a voicemail provider as
1647          opposed to silently falling back to default one. */
1648         if (provider == null) {
1649             mVoicemailProviders.setSummary(getString(R.string.sum_voicemail_choose_provider));
1650             mVoicemailSettings.setSummary("");
1651             mVoicemailSettings.setEnabled(false);
1652             mVoicemailSettings.setIntent(null);
1653
1654             mVoicemailNotificationVibrateWhen.setEnabled(false);
1655             mVoicemailNotificationVibrateWhen.setSummary("");
1656         } else {
1657             final String providerName = provider.name;
1658             mVoicemailProviders.setSummary(providerName);
1659             mVoicemailSettings.setSummary(getApplicationContext().getString(
1660                     R.string.voicemail_settings_for, providerName));
1661             mVoicemailSettings.setEnabled(true);
1662             mVoicemailSettings.setIntent(provider.intent);
1663
1664             mVoicemailNotificationVibrateWhen.setEnabled(true);
1665             mVoicemailNotificationVibrateWhen.setSummary(
1666                     mVoicemailNotificationVibrateWhen.getEntry());
1667         }
1668     }
1669
1670     /**
1671      * Enumerates existing VM providers and puts their data into the list and populates
1672      * the preference list objects with their names.
1673      * In case we are called with ACTION_ADD_VOICEMAIL intent the intent may have
1674      * an extra string called IGNORE_PROVIDER_EXTRA with "package.activityName" of the provider
1675      * which should be hidden when we bring up the list of possible VM providers to choose.
1676      * This allows a provider which is being disabled (e.g. GV user logging out) to force the user
1677      * to pick some other provider.
1678      */
1679     private void initVoiceMailProviders() {
1680         mPerProviderSavedVMNumbers =
1681             this.getApplicationContext().getSharedPreferences(
1682                 VM_NUMBERS_SHARED_PREFERENCES_NAME, MODE_PRIVATE);
1683
1684         String providerToIgnore = null;
1685         if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) {
1686             if (DBG) log("ACTION_ADD_VOICEMAIL");
1687             if (getIntent().hasExtra(IGNORE_PROVIDER_EXTRA)) {
1688                 providerToIgnore = getIntent().getStringExtra(IGNORE_PROVIDER_EXTRA);
1689             }
1690             if (DBG) log("providerToIgnore=" + providerToIgnore);
1691             if (providerToIgnore != null) {
1692                 deleteSettingsForVoicemailProvider(providerToIgnore);
1693             }
1694         }
1695
1696         mVMProvidersData.clear();
1697
1698         // Stick the default element which is always there
1699         final String myCarrier = getString(R.string.voicemail_default);
1700         mVMProvidersData.put(DEFAULT_VM_PROVIDER_KEY, new VoiceMailProvider(myCarrier, null));
1701
1702         // Enumerate providers
1703         PackageManager pm = getPackageManager();
1704         Intent intent = new Intent();
1705         intent.setAction(ACTION_CONFIGURE_VOICEMAIL);
1706         List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
1707         int len = resolveInfos.size() + 1; // +1 for the default choice we will insert.
1708
1709         // Go through the list of discovered providers populating the data map
1710         // skip the provider we were instructed to ignore if there was one
1711         for (int i = 0; i < resolveInfos.size(); i++) {
1712             final ResolveInfo ri= resolveInfos.get(i);
1713             final ActivityInfo currentActivityInfo = ri.activityInfo;
1714             final String key = makeKeyForActivity(currentActivityInfo);
1715             if (DBG) log("Loading " + key);
1716             if (key.equals(providerToIgnore)) {
1717                 if (DBG) log("Ignoring " + key);
1718                 len--;
1719                 continue;
1720             }
1721             final String nameForDisplay = ri.loadLabel(pm).toString();
1722             Intent providerIntent = new Intent();
1723             providerIntent.setAction(ACTION_CONFIGURE_VOICEMAIL);
1724             providerIntent.setClassName(currentActivityInfo.packageName,
1725                     currentActivityInfo.name);
1726             mVMProvidersData.put(
1727                     key,
1728                     new VoiceMailProvider(nameForDisplay, providerIntent));
1729
1730         }
1731
1732         // Now we know which providers to display - create entries and values array for
1733         // the list preference
1734         String [] entries = new String [len];
1735         String [] values = new String [len];
1736         entries[0] = myCarrier;
1737         values[0] = DEFAULT_VM_PROVIDER_KEY;
1738         int entryIdx = 1;
1739         for (int i = 0; i < resolveInfos.size(); i++) {
1740             final String key = makeKeyForActivity(resolveInfos.get(i).activityInfo);
1741             if (!mVMProvidersData.containsKey(key)) {
1742                 continue;
1743             }
1744             entries[entryIdx] = mVMProvidersData.get(key).name;
1745             values[entryIdx] = key;
1746             entryIdx++;
1747         }
1748
1749         mVoicemailProviders.setEntries(entries);
1750         mVoicemailProviders.setEntryValues(values);
1751
1752         mPreviousVMProviderKey = getCurrentVoicemailProviderKey();
1753         updateVMPreferenceWidgets(mPreviousVMProviderKey);
1754     }
1755
1756     private String makeKeyForActivity(ActivityInfo ai) {
1757         return ai.name;
1758     }
1759
1760     /**
1761      * Simulates user clicking on a passed preference.
1762      * Usually needed when the preference is a dialog preference and we want to invoke
1763      * a dialog for this preference programmatically.
1764      * TODO(iliat): figure out if there is a cleaner way to cause preference dlg to come up
1765      */
1766     private void simulatePreferenceClick(Preference preference) {
1767         // Go through settings until we find our setting
1768         // and then simulate a click on it to bring up the dialog
1769         final ListAdapter adapter = getPreferenceScreen().getRootAdapter();
1770         for (int idx = 0; idx < adapter.getCount(); idx++) {
1771             if (adapter.getItem(idx) == preference) {
1772                 getPreferenceScreen().onItemClick(this.getListView(),
1773                         null, idx, adapter.getItemId(idx));
1774                 break;
1775             }
1776         }
1777     }
1778
1779     /**
1780      * Saves new VM provider settings associating them with the currently selected
1781      * provider if settings are different than the ones already stored for this
1782      * provider.
1783      * Later on these will be used when the user switches a provider.
1784      */
1785     private void maybeSaveSettingsForVoicemailProvider(String key,
1786             VoiceMailProviderSettings newSettings) {
1787         if (mVoicemailProviders == null) {
1788             return;
1789         }
1790         final VoiceMailProviderSettings curSettings = loadSettingsForVoiceMailProvider(key);
1791         if (newSettings.equals(curSettings)) {
1792             if (DBG) log("Not saving setting for " + key + " since they have not changed");
1793             return;
1794         }
1795         if (DBG) log("Saving settings for " + key + ": " + newSettings.toString());
1796         Editor editor = mPerProviderSavedVMNumbers.edit();
1797         editor.putString(key + VM_NUMBER_TAG,newSettings.voicemailNumber);
1798         String fwdKey = key + FWD_SETTINGS_TAG;
1799         CallForwardInfo[] s = newSettings.forwardingSettings;
1800         if (s != FWD_SETTINGS_DONT_TOUCH) {
1801             editor.putInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, s.length);
1802             for (int i = 0; i < s.length; i++) {
1803                 final String settingKey = fwdKey + FWD_SETTING_TAG + String.valueOf(i);
1804                 final CallForwardInfo fi = s[i];
1805                 editor.putInt(settingKey + FWD_SETTING_STATUS, fi.status);
1806                 editor.putInt(settingKey + FWD_SETTING_REASON, fi.reason);
1807                 editor.putString(settingKey + FWD_SETTING_NUMBER, fi.number);
1808                 editor.putInt(settingKey + FWD_SETTING_TIME, fi.timeSeconds);
1809             }
1810         } else {
1811             editor.putInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, 0);
1812         }
1813         editor.apply();
1814     }
1815
1816     /**
1817      * Returns settings previously stored for the currently selected
1818      * voice mail provider. If none is stored returns null.
1819      * If the user switches to a voice mail provider and we have settings
1820      * stored for it we will automatically change the phone's voice mail number
1821      * and forwarding number to the stored one. Otherwise we will bring up provider's configuration
1822      * UI.
1823      */
1824     private VoiceMailProviderSettings loadSettingsForVoiceMailProvider(String key) {
1825         final String vmNumberSetting = mPerProviderSavedVMNumbers.getString(key + VM_NUMBER_TAG,
1826                 null);
1827         if (vmNumberSetting == null) {
1828             if (DBG) log("Settings for " + key + " not found");
1829             return null;
1830         }
1831
1832         CallForwardInfo[] cfi = FWD_SETTINGS_DONT_TOUCH;
1833         String fwdKey = key + FWD_SETTINGS_TAG;
1834         final int fwdLen = mPerProviderSavedVMNumbers.getInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, 0);
1835         if (fwdLen > 0) {
1836             cfi = new CallForwardInfo[fwdLen];
1837             for (int i = 0; i < cfi.length; i++) {
1838                 final String settingKey = fwdKey + FWD_SETTING_TAG + String.valueOf(i);
1839                 cfi[i] = new CallForwardInfo();
1840                 cfi[i].status = mPerProviderSavedVMNumbers.getInt(
1841                         settingKey + FWD_SETTING_STATUS, 0);
1842                 cfi[i].reason = mPerProviderSavedVMNumbers.getInt(
1843                         settingKey + FWD_SETTING_REASON,
1844                         CommandsInterface.CF_REASON_ALL_CONDITIONAL);
1845                 cfi[i].serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
1846                 cfi[i].toa = PhoneNumberUtils.TOA_International;
1847                 cfi[i].number = mPerProviderSavedVMNumbers.getString(
1848                         settingKey + FWD_SETTING_NUMBER, "");
1849                 cfi[i].timeSeconds = mPerProviderSavedVMNumbers.getInt(
1850                         settingKey + FWD_SETTING_TIME, 20);
1851             }
1852         }
1853
1854         VoiceMailProviderSettings settings =  new VoiceMailProviderSettings(vmNumberSetting, cfi);
1855         if (DBG) log("Loaded settings for " + key + ": " + settings.toString());
1856         return settings;
1857     }
1858
1859     /**
1860      * Deletes settings for the specified provider.
1861      */
1862     private void deleteSettingsForVoicemailProvider(String key) {
1863         if (DBG) log("Deleting settings for" + key);
1864         if (mVoicemailProviders == null) {
1865             return;
1866         }
1867         mPerProviderSavedVMNumbers.edit()
1868             .putString(key + VM_NUMBER_TAG, null)
1869             .putInt(key + FWD_SETTINGS_TAG + FWD_SETTINGS_LENGTH_TAG, 0)
1870             .commit();
1871     }
1872
1873     private String getCurrentVoicemailProviderKey() {
1874         final String key = mVoicemailProviders.getValue();
1875         return (key != null) ? key : DEFAULT_VM_PROVIDER_KEY;
1876     }
1877
1878     @Override
1879     public boolean onOptionsItemSelected(MenuItem item) {
1880         final int itemId = item.getItemId();
1881         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
1882             Intent intent = new Intent();
1883             intent.setClassName(UP_ACTIVITY_PACKAGE, UP_ACTIVITY_CLASS);
1884             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1885             startActivity(intent);
1886             finish();
1887             return true;
1888         }
1889         return super.onOptionsItemSelected(item);
1890     }
1891
1892     /**
1893      * Finish current Activity and go up to the top level Settings ({@link CallFeaturesSetting}).
1894      * This is useful for implementing "HomeAsUp" capability for second-level Settings.
1895      */
1896     public static void goUpToTopLevelSetting(Activity activity) {
1897         Intent intent = new Intent(activity, CallFeaturesSetting.class);
1898         intent.setAction(Intent.ACTION_MAIN);
1899         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1900         activity.startActivity(intent);
1901         activity.finish();
1902     }
1903 }