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