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