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