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