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