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