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