Home | History | Annotate | Download | only in phone
      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 {
    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          */
    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 
    269         public VoiceMailProviderSettings(String voicemailNumber, CallForwardInfo[] infos) {
    270             this.voicemailNumber = voicemailNumber;
    271             this.forwardingSettings = infos;
    272         }
    273 
    274         @Override
    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 
    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
    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
    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
    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      */
    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.
    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      */
    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
    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 
    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 
    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
    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
    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.)
    756     private void showDialogIfForeground(int id) {
    757         if (mForeground) {
    758             showDialog(id);
    759         }
    760     }
    761 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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      */
   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      */
   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      */
   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 
   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 
   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 
   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.
   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
   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
   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.
   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.
   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
   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 
   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.
   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
   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 
   1563     private boolean isAirplaneModeOn() {
   1564         return Settings.System.getInt(getContentResolver(),
   1565                 Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   1566     }
   1567 
   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 
   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 
   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 
   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      */
   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      */
   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 
   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      */
   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      */
   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      */
   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      */
   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 
   1856     private String getCurrentVoicemailProviderKey() {
   1857         final String key = mVoicemailProviders.getValue();
   1858         return (key != null) ? key : DEFAULT_VM_PROVIDER_KEY;
   1859     }
   1860 
   1861     @Override
   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      */
   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