Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.phone;
     18 
     19 import android.app.ActionBar;
     20 import android.app.Activity;
     21 import android.app.ActivityOptions;
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.database.Cursor;
     29 import android.media.AudioManager;
     30 import android.os.AsyncResult;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.os.UserHandle;
     35 import android.preference.CheckBoxPreference;
     36 import android.preference.ListPreference;
     37 import android.preference.Preference;
     38 import android.preference.PreferenceActivity;
     39 import android.preference.PreferenceScreen;
     40 import android.provider.ContactsContract.CommonDataKinds;
     41 import android.provider.Settings;
     42 import android.telecom.PhoneAccountHandle;
     43 import android.telecom.TelecomManager;
     44 import android.telephony.PhoneStateListener;
     45 import android.telephony.TelephonyManager;
     46 import android.text.TextUtils;
     47 import android.util.Log;
     48 import android.view.MenuItem;
     49 import android.widget.ListAdapter;
     50 import android.widget.Toast;
     51 
     52 import com.android.ims.ImsManager;
     53 import com.android.internal.telephony.CallForwardInfo;
     54 import com.android.internal.telephony.Phone;
     55 import com.android.internal.telephony.PhoneConstants;
     56 import com.android.phone.common.util.SettingsUtil;
     57 import com.android.phone.settings.AccountSelectionPreference;
     58 import com.android.phone.settings.CallForwardInfoUtil;
     59 import com.android.phone.settings.TtyModeListPreference;
     60 import com.android.phone.settings.VoicemailDialogUtil;
     61 import com.android.phone.settings.VoicemailNotificationSettingsUtil;
     62 import com.android.phone.settings.VoicemailProviderListPreference;
     63 import com.android.phone.settings.VoicemailProviderListPreference.VoicemailProvider;
     64 import com.android.phone.settings.VoicemailProviderSettings;
     65 import com.android.phone.settings.VoicemailProviderSettingsUtil;
     66 import com.android.phone.settings.VoicemailRingtonePreference;
     67 import com.android.phone.settings.fdn.FdnSetting;
     68 import com.android.services.telephony.sip.SipUtil;
     69 
     70 import java.lang.String;
     71 import java.util.ArrayList;
     72 import java.util.Collection;
     73 import java.util.HashMap;
     74 import java.util.HashSet;
     75 import java.util.Iterator;
     76 import java.util.List;
     77 import java.util.Map;
     78 
     79 /**
     80  * Top level "Call settings" UI; see res/xml/call_feature_setting.xml
     81  *
     82  * This preference screen is the root of the "Call settings" hierarchy available from the Phone
     83  * app; the settings here let you control various features related to phone calls (including
     84  * voicemail settings, the "Respond via SMS" feature, and others.)  It's used only on
     85  * voice-capable phone devices.
     86  *
     87  * Note that this activity is part of the package com.android.phone, even
     88  * though you reach it from the "Phone" app (i.e. DialtactsActivity) which
     89  * is from the package com.android.contacts.
     90  *
     91  * For the "Mobile network settings" screen under the main Settings app,
     92  * See {@link MobileNetworkSettings}.
     93  *
     94  * TODO: Settings should be split into PreferenceFragments where possible (ie. voicemail).
     95  *
     96  * @see com.android.phone.MobileNetworkSettings
     97  */
     98 public class CallFeaturesSetting extends PreferenceActivity
     99         implements DialogInterface.OnClickListener,
    100                 Preference.OnPreferenceChangeListener,
    101                 EditPhoneNumberPreference.OnDialogClosedListener,
    102                 EditPhoneNumberPreference.GetDefaultNumberListener {
    103     private static final String LOG_TAG = "CallFeaturesSetting";
    104     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
    105     // STOPSHIP if true. Flag to override behavior default behavior to hide VT setting.
    106     private static final boolean ENABLE_VT_FLAG = false;
    107 
    108     /**
    109      * Intent action to bring up Voicemail Provider settings.
    110      *
    111      * @see #IGNORE_PROVIDER_EXTRA
    112      */
    113     public static final String ACTION_ADD_VOICEMAIL =
    114             "com.android.phone.CallFeaturesSetting.ADD_VOICEMAIL";
    115     // intent action sent by this activity to a voice mail provider
    116     // to trigger its configuration UI
    117     public static final String ACTION_CONFIGURE_VOICEMAIL =
    118             "com.android.phone.CallFeaturesSetting.CONFIGURE_VOICEMAIL";
    119     // Extra put in the return from VM provider config containing voicemail number to set
    120     public static final String VM_NUMBER_EXTRA = "com.android.phone.VoicemailNumber";
    121     // Extra put in the return from VM provider config containing call forwarding number to set
    122     public static final String FWD_NUMBER_EXTRA = "com.android.phone.ForwardingNumber";
    123     // Extra put in the return from VM provider config containing call forwarding number to set
    124     public static final String FWD_NUMBER_TIME_EXTRA = "com.android.phone.ForwardingNumberTime";
    125     // If the VM provider returns non null value in this extra we will force the user to
    126     // choose another VM provider
    127     public static final String SIGNOUT_EXTRA = "com.android.phone.Signout";
    128 
    129     /**
    130      * String Extra put into ACTION_ADD_VOICEMAIL call to indicate which provider should be hidden
    131      * in the list of providers presented to the user. This allows a provider which is being
    132      * disabled (e.g. GV user logging out) to force the user to pick some other provider.
    133      */
    134     public static final String IGNORE_PROVIDER_EXTRA = "com.android.phone.ProviderToIgnore";
    135 
    136     /**
    137      * String Extra put into ACTION_ADD_VOICEMAIL to indicate that the voicemail setup screen should
    138      * be opened.
    139      */
    140     public static final String SETUP_VOICEMAIL_EXTRA = "com.android.phone.SetupVoicemail";
    141 
    142     // string constants
    143     private static final String NUM_PROJECTION[] = {CommonDataKinds.Phone.NUMBER};
    144 
    145     // String keys for preference lookup
    146     // TODO: Naming these "BUTTON_*" is confusing since they're not actually buttons(!)
    147     // TODO: Consider moving these strings to strings.xml, so that they are not duplicated here and
    148     // in the layout files. These strings need to be treated carefully; if the setting is
    149     // persistent, they are used as the key to store shared preferences and the name should not be
    150     // changed unless the settings are also migrated.
    151     private static final String VOICEMAIL_SETTING_SCREEN_PREF_KEY = "button_voicemail_category_key";
    152     private static final String BUTTON_VOICEMAIL_KEY = "button_voicemail_key";
    153     private static final String BUTTON_VOICEMAIL_PROVIDER_KEY = "button_voicemail_provider_key";
    154     private static final String BUTTON_VOICEMAIL_SETTING_KEY = "button_voicemail_setting_key";
    155     private static final String BUTTON_FDN_KEY   = "button_fdn_key";
    156 
    157     private static final String BUTTON_DTMF_KEY        = "button_dtmf_settings";
    158     private static final String BUTTON_RETRY_KEY       = "button_auto_retry_key";
    159     private static final String BUTTON_TTY_KEY         = "button_tty_mode_key";
    160     private static final String BUTTON_HAC_KEY         = "button_hac_key";
    161 
    162     private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
    163     private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
    164     private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
    165     private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
    166 
    167     private static final String PHONE_ACCOUNT_SETTINGS_KEY =
    168             "phone_account_settings_preference_screen";
    169 
    170     private static final String ENABLE_VIDEO_CALLING_KEY = "button_enable_video_calling";
    171 
    172     /** Event for Async voicemail change call */
    173     private static final int EVENT_VOICEMAIL_CHANGED        = 500;
    174     private static final int EVENT_FORWARDING_CHANGED       = 501;
    175     private static final int EVENT_FORWARDING_GET_COMPLETED = 502;
    176 
    177     public static final String HAC_KEY = "HACSetting";
    178     public static final String HAC_VAL_ON = "ON";
    179     public static final String HAC_VAL_OFF = "OFF";
    180 
    181     /** Handle to voicemail pref */
    182     private static final int VOICEMAIL_PREF_ID = 1;
    183     private static final int VOICEMAIL_PROVIDER_CFG_ID = 2;
    184 
    185     private Phone mPhone;
    186     private AudioManager mAudioManager;
    187 
    188     private SubscriptionInfoHelper mSubscriptionInfoHelper;
    189 
    190     private EditPhoneNumberPreference mSubMenuVoicemailSettings;
    191 
    192     /** Whether dialpad plays DTMF tone or not. */
    193     private CheckBoxPreference mButtonAutoRetry;
    194     private CheckBoxPreference mButtonHAC;
    195     private ListPreference mButtonDTMF;
    196     private TtyModeListPreference mButtonTTY;
    197     private VoicemailProviderListPreference mVoicemailProviders;
    198     private PreferenceScreen mVoicemailSettingsScreen;
    199     private PreferenceScreen mVoicemailSettings;
    200     private VoicemailRingtonePreference mVoicemailNotificationRingtone;
    201     private CheckBoxPreference mVoicemailNotificationVibrate;
    202     private CheckBoxPreference mEnableVideoCalling;
    203 
    204     /**
    205      * Results of reading forwarding settings
    206      */
    207     private CallForwardInfo[] mForwardingReadResults = null;
    208 
    209     /**
    210      * Result of forwarding number change.
    211      * Keys are reasons (eg. unconditional forwarding).
    212      */
    213     private Map<Integer, AsyncResult> mForwardingChangeResults = null;
    214 
    215     /**
    216      * Expected CF read result types.
    217      * This set keeps track of the CF types for which we've issued change
    218      * commands so we can tell when we've received all of the responses.
    219      */
    220     private Collection<Integer> mExpectedChangeResultReasons = null;
    221 
    222     /**
    223      * Result of vm number change
    224      */
    225     private AsyncResult mVoicemailChangeResult = null;
    226 
    227     /**
    228      * Previous VM provider setting so we can return to it in case of failure.
    229      */
    230     private String mPreviousVMProviderKey = null;
    231 
    232     /**
    233      * Id of the dialog being currently shown.
    234      */
    235     private int mCurrentDialogId = 0;
    236 
    237     /**
    238      * Flag indicating that we are invoking settings for the voicemail provider programmatically
    239      * due to vm provider change.
    240      */
    241     private boolean mVMProviderSettingsForced = false;
    242 
    243     /**
    244      * Flag indicating that we are making changes to vm or fwd numbers
    245      * due to vm provider change.
    246      */
    247     private boolean mChangingVMorFwdDueToProviderChange = false;
    248 
    249     /**
    250      * True if we are in the process of vm & fwd number change and vm has already been changed.
    251      * This is used to decide what to do in case of rollback.
    252      */
    253     private boolean mVMChangeCompletedSuccessfully = false;
    254 
    255     /**
    256      * True if we had full or partial failure setting forwarding numbers and so need to roll them
    257      * back.
    258      */
    259     private boolean mFwdChangesRequireRollback = false;
    260 
    261     /**
    262      * Id of error msg to display to user once we are done reverting the VM provider to the previous
    263      * one.
    264      */
    265     private int mVMOrFwdSetError = 0;
    266 
    267     /** string to hold old voicemail number as it is being updated. */
    268     private String mOldVmNumber;
    269 
    270     // New call forwarding settings and vm number we will be setting
    271     // Need to save these since before we get to saving we need to asynchronously
    272     // query the existing forwarding settings.
    273     private CallForwardInfo[] mNewFwdSettings;
    274     private String mNewVMNumber;
    275 
    276     private boolean mForeground;
    277 
    278     @Override
    279     public void onPause() {
    280         super.onPause();
    281         mForeground = false;
    282 
    283         if (ImsManager.isVolteEnabledByPlatform(this) &&
    284                 !mPhone.getContext().getResources().getBoolean(
    285                         com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
    286             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    287             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
    288         }
    289     }
    290 
    291     /**
    292      * We have to pull current settings from the network for all kinds of
    293      * voicemail providers so we can tell whether we have to update them,
    294      * so use this bit to keep track of whether we're reading settings for the
    295      * default provider and should therefore save them out when done.
    296      */
    297     private boolean mReadingSettingsForDefaultProvider = false;
    298 
    299     /**
    300      * Used to indicate that the voicemail preference should be shown.
    301      */
    302     private boolean mShowVoicemailPreference = false;
    303 
    304     /**
    305      * Used to indicate that the voicemail setup screen should be shown.
    306      */
    307     private boolean mSetupVoicemail = false;
    308 
    309     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    310         /**
    311          * Enable/disable the TTY setting when in/out of a call (and if carrier doesn't
    312          * support VoLTE with TTY).
    313          * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
    314          * java.lang.String)
    315          */
    316         @Override
    317         public void onCallStateChanged(int state, String incomingNumber) {
    318             if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state);
    319             Preference pref = getPreferenceScreen().findPreference(BUTTON_TTY_KEY);
    320             if (pref != null) {
    321                 pref.setEnabled(state == TelephonyManager.CALL_STATE_IDLE);
    322             }
    323         }
    324     };
    325 
    326     /*
    327      * Click Listeners, handle click based on objects attached to UI.
    328      */
    329 
    330     // Click listener for all toggle events
    331     @Override
    332     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    333         if (preference == mSubMenuVoicemailSettings) {
    334             return true;
    335         } else if (preference == mButtonDTMF) {
    336             return true;
    337         } else if (preference == mButtonTTY) {
    338             return true;
    339         } else if (preference == mButtonAutoRetry) {
    340             android.provider.Settings.Global.putInt(mPhone.getContext().getContentResolver(),
    341                     android.provider.Settings.Global.CALL_AUTO_RETRY,
    342                     mButtonAutoRetry.isChecked() ? 1 : 0);
    343             return true;
    344         } else if (preference == mButtonHAC) {
    345             int hac = mButtonHAC.isChecked() ? 1 : 0;
    346             // Update HAC value in Settings database
    347             Settings.System.putInt(mPhone.getContext().getContentResolver(),
    348                     Settings.System.HEARING_AID, hac);
    349 
    350             // Update HAC Value in AudioManager
    351             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
    352             return true;
    353         } else if (preference.getKey().equals(mVoicemailSettings.getKey())) {
    354             // Check key instead of comparing reference because closing the voicemail notification
    355             // ringtone dialog invokes onResume(), but leaves the old preference screen up,
    356             // TODO: Revert to checking reference after migrating voicemail to its own activity.
    357             if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
    358 
    359             final Dialog dialog = ((PreferenceScreen) preference).getDialog();
    360             if (dialog != null) {
    361                 dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
    362             }
    363 
    364             if (preference.getIntent() != null) {
    365                 if (DBG) log("Invoking cfg intent " + preference.getIntent().getPackage());
    366 
    367                 // onActivityResult() will be responsible for resetting some of variables.
    368                 this.startActivityForResult(preference.getIntent(), VOICEMAIL_PROVIDER_CFG_ID);
    369                 return true;
    370             } else {
    371                 if (DBG) log("onPreferenceTreeClick(). No intent; use default behavior in xml.");
    372 
    373                 // onActivityResult() will not be called, so reset variables here.
    374                 mPreviousVMProviderKey = VoicemailProviderListPreference.DEFAULT_KEY;
    375                 mVMProviderSettingsForced = false;
    376                 return false;
    377             }
    378         } else if (preference == mVoicemailSettingsScreen) {
    379             final Dialog dialog = mVoicemailSettingsScreen.getDialog();
    380             if (dialog != null) {
    381                 dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
    382             }
    383             return false;
    384         }
    385         return false;
    386     }
    387 
    388     /**
    389      * Implemented to support onPreferenceChangeListener to look for preference
    390      * changes.
    391      *
    392      * @param preference is the preference to be changed
    393      * @param objValue should be the value of the selection, NOT its localized
    394      * display value.
    395      */
    396     @Override
    397     public boolean onPreferenceChange(Preference preference, Object objValue) {
    398         if (DBG) log("onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\"");
    399 
    400         if (preference == mButtonDTMF) {
    401             int index = mButtonDTMF.findIndexOfValue((String) objValue);
    402             Settings.System.putInt(mPhone.getContext().getContentResolver(),
    403                     Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, index);
    404         } else if (preference == mVoicemailProviders) {
    405             final String newProviderKey = (String) objValue;
    406 
    407             // If previous provider key and the new one is same, we don't need to handle it.
    408             if (mPreviousVMProviderKey.equals(newProviderKey)) {
    409                 if (DBG) log("No change is made to the VM provider setting.");
    410                 return true;
    411             }
    412             updateVMPreferenceWidgets(newProviderKey);
    413 
    414             final VoicemailProviderSettings newProviderSettings =
    415                     VoicemailProviderSettingsUtil.load(this, newProviderKey);
    416 
    417             // If the user switches to a voice mail provider and we have numbers stored for it we
    418             // will automatically change the phone's voice mail and forwarding number to the stored
    419             // ones. Otherwise we will bring up provider's configuration UI.
    420             if (newProviderSettings == null) {
    421                 // Force the user into a configuration of the chosen provider
    422                 Log.w(LOG_TAG, "Saved preferences not found - invoking config");
    423                 mVMProviderSettingsForced = true;
    424                 simulatePreferenceClick(mVoicemailSettings);
    425             } else {
    426                 if (DBG) log("Saved preferences found - switching to them");
    427                 // Set this flag so if we get a failure we revert to previous provider
    428                 mChangingVMorFwdDueToProviderChange = true;
    429                 saveVoiceMailAndForwardingNumber(newProviderKey, newProviderSettings);
    430             }
    431         } else if (preference.getKey().equals(mVoicemailNotificationVibrate.getKey())) {
    432             // Check key instead of comparing reference because closing the voicemail notification
    433             // ringtone dialog invokes onResume(), but leaves the old preference screen up,
    434             // TODO: Revert to checking reference after migrating voicemail to its own activity.
    435             VoicemailNotificationSettingsUtil.setVibrationEnabled(
    436                     mPhone, Boolean.TRUE.equals(objValue));
    437         } else if (preference == mEnableVideoCalling) {
    438             if (ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mPhone.getContext())) {
    439                 PhoneGlobals.getInstance().phoneMgr.enableVideoCalling((boolean) objValue);
    440             } else {
    441                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
    442                 DialogInterface.OnClickListener networkSettingsClickListener =
    443                         new Dialog.OnClickListener() {
    444                             @Override
    445                             public void onClick(DialogInterface dialog, int which) {
    446                                 startActivity(new Intent(mPhone.getContext(),
    447                                         com.android.phone.MobileNetworkSettings.class));
    448                             }
    449                         };
    450                 builder.setMessage(getResources().getString(
    451                                 R.string.enable_video_calling_dialog_msg))
    452                         .setNeutralButton(getResources().getString(
    453                                 R.string.enable_video_calling_dialog_settings),
    454                                 networkSettingsClickListener)
    455                         .setPositiveButton(android.R.string.ok, null)
    456                         .show();
    457                 return false;
    458             }
    459         }
    460 
    461         // Always let the preference setting proceed.
    462         return true;
    463     }
    464 
    465     @Override
    466     public void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked) {
    467         if (DBG) log("onDialogClosed: Button clicked is " + buttonClicked);
    468 
    469         if (buttonClicked == DialogInterface.BUTTON_NEGATIVE) {
    470             return;
    471         }
    472 
    473         if (preference == mSubMenuVoicemailSettings) {
    474             VoicemailProviderSettings newSettings = new VoicemailProviderSettings(
    475                     mSubMenuVoicemailSettings.getPhoneNumber(),
    476                     VoicemailProviderSettings.NO_FORWARDING);
    477             saveVoiceMailAndForwardingNumber(mVoicemailProviders.getKey(), newSettings);
    478         }
    479     }
    480 
    481     /**
    482      * Implemented for EditPhoneNumberPreference.GetDefaultNumberListener.
    483      * This method set the default values for the various
    484      * EditPhoneNumberPreference dialogs.
    485      */
    486     @Override
    487     public String onGetDefaultNumber(EditPhoneNumberPreference preference) {
    488         if (preference == mSubMenuVoicemailSettings) {
    489             // update the voicemail number field, which takes care of the
    490             // mSubMenuVoicemailSettings itself, so we should return null.
    491             if (DBG) log("updating default for voicemail dialog");
    492             updateVoiceNumberField();
    493             return null;
    494         }
    495 
    496         String vmDisplay = mPhone.getVoiceMailNumber();
    497         if (TextUtils.isEmpty(vmDisplay)) {
    498             // if there is no voicemail number, we just return null to
    499             // indicate no contribution.
    500             return null;
    501         }
    502 
    503         // Return the voicemail number prepended with "VM: "
    504         if (DBG) log("updating default for call forwarding dialogs");
    505         return getString(R.string.voicemail_abbreviated) + " " + vmDisplay;
    506     }
    507 
    508     private void switchToPreviousVoicemailProvider() {
    509         if (DBG) log("switchToPreviousVoicemailProvider " + mPreviousVMProviderKey);
    510 
    511         if (mPreviousVMProviderKey == null) {
    512             return;
    513         }
    514 
    515         if (mVMChangeCompletedSuccessfully || mFwdChangesRequireRollback) {
    516             showDialogIfForeground(VoicemailDialogUtil.VM_REVERTING_DIALOG);
    517             final VoicemailProviderSettings prevSettings =
    518                     VoicemailProviderSettingsUtil.load(this, mPreviousVMProviderKey);
    519             if (prevSettings == null) {
    520                 Log.e(LOG_TAG, "VoicemailProviderSettings for the key \""
    521                         + mPreviousVMProviderKey + "\" is null but should be loaded.");
    522             }
    523 
    524             if (mVMChangeCompletedSuccessfully) {
    525                 mNewVMNumber = prevSettings.getVoicemailNumber();
    526                 Log.i(LOG_TAG, "VM change is already completed successfully."
    527                         + "Have to revert VM back to " + mNewVMNumber + " again.");
    528                 mPhone.setVoiceMailNumber(
    529                         mPhone.getVoiceMailAlphaTag().toString(),
    530                         mNewVMNumber,
    531                         Message.obtain(mRevertOptionComplete, EVENT_VOICEMAIL_CHANGED));
    532             }
    533 
    534             if (mFwdChangesRequireRollback) {
    535                 Log.i(LOG_TAG, "Requested to rollback forwarding changes.");
    536 
    537                 final CallForwardInfo[] prevFwdSettings = prevSettings.getForwardingSettings();
    538                 if (prevFwdSettings != null) {
    539                     Map<Integer, AsyncResult> results = mForwardingChangeResults;
    540                     resetForwardingChangeState();
    541                     for (int i = 0; i < prevFwdSettings.length; i++) {
    542                         CallForwardInfo fi = prevFwdSettings[i];
    543                         if (DBG) log("Reverting fwd #: " + i + ": " + fi.toString());
    544                         // Only revert the settings for which the update succeeded.
    545                         AsyncResult result = results.get(fi.reason);
    546                         if (result != null && result.exception == null) {
    547                             mExpectedChangeResultReasons.add(fi.reason);
    548                             CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
    549                                     mRevertOptionComplete.obtainMessage(
    550                                             EVENT_FORWARDING_CHANGED, i, 0));
    551                         }
    552                     }
    553                 }
    554             }
    555         } else {
    556             if (DBG) log("No need to revert");
    557             onRevertDone();
    558         }
    559     }
    560 
    561     private void onRevertDone() {
    562         if (DBG) log("onRevertDone: Changing provider key back to " + mPreviousVMProviderKey);
    563 
    564         updateVMPreferenceWidgets(mPreviousVMProviderKey);
    565         updateVoiceNumberField();
    566         if (mVMOrFwdSetError != 0) {
    567             showDialogIfForeground(mVMOrFwdSetError);
    568             mVMOrFwdSetError = 0;
    569         }
    570     }
    571 
    572     @Override
    573     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    574         if (DBG) {
    575             log("onActivityResult: requestCode: " + requestCode
    576                     + ", resultCode: " + resultCode
    577                     + ", data: " + data);
    578         }
    579 
    580         // there are cases where the contact picker may end up sending us more than one
    581         // request.  We want to ignore the request if we're not in the correct state.
    582         if (requestCode == VOICEMAIL_PROVIDER_CFG_ID) {
    583             boolean failure = false;
    584 
    585             // No matter how the processing of result goes lets clear the flag
    586             if (DBG) log("mVMProviderSettingsForced: " + mVMProviderSettingsForced);
    587             final boolean isVMProviderSettingsForced = mVMProviderSettingsForced;
    588             mVMProviderSettingsForced = false;
    589 
    590             String vmNum = null;
    591             if (resultCode != RESULT_OK) {
    592                 if (DBG) log("onActivityResult: vm provider cfg result not OK.");
    593                 failure = true;
    594             } else {
    595                 if (data == null) {
    596                     if (DBG) log("onActivityResult: vm provider cfg result has no data");
    597                     failure = true;
    598                 } else {
    599                     if (data.getBooleanExtra(SIGNOUT_EXTRA, false)) {
    600                         if (DBG) log("Provider requested signout");
    601                         if (isVMProviderSettingsForced) {
    602                             if (DBG) log("Going back to previous provider on signout");
    603                             switchToPreviousVoicemailProvider();
    604                         } else {
    605                             final String victim = mVoicemailProviders.getKey();
    606                             if (DBG) log("Relaunching activity and ignoring " + victim);
    607                             Intent i = new Intent(ACTION_ADD_VOICEMAIL);
    608                             i.putExtra(IGNORE_PROVIDER_EXTRA, victim);
    609                             i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    610                             this.startActivity(i);
    611                         }
    612                         return;
    613                     }
    614                     vmNum = data.getStringExtra(VM_NUMBER_EXTRA);
    615                     if (vmNum == null || vmNum.length() == 0) {
    616                         if (DBG) log("onActivityResult: vm provider cfg result has no vmnum");
    617                         failure = true;
    618                     }
    619                 }
    620             }
    621             if (failure) {
    622                 if (DBG) log("Failure in return from voicemail provider.");
    623                 if (isVMProviderSettingsForced) {
    624                     switchToPreviousVoicemailProvider();
    625                 }
    626 
    627                 return;
    628             }
    629             mChangingVMorFwdDueToProviderChange = isVMProviderSettingsForced;
    630             final String fwdNum = data.getStringExtra(FWD_NUMBER_EXTRA);
    631 
    632             // TODO: It would be nice to load the current network setting for this and
    633             // send it to the provider when it's config is invoked so it can use this as default
    634             final int fwdNumTime = data.getIntExtra(FWD_NUMBER_TIME_EXTRA, 20);
    635 
    636             if (DBG) log("onActivityResult: cfg result has forwarding number " + fwdNum);
    637             saveVoiceMailAndForwardingNumber(mVoicemailProviders.getKey(),
    638                     new VoicemailProviderSettings(vmNum, fwdNum, fwdNumTime));
    639             return;
    640         }
    641 
    642         if (requestCode == VOICEMAIL_PREF_ID) {
    643             if (resultCode != RESULT_OK) {
    644                 if (DBG) log("onActivityResult: contact picker result not OK.");
    645                 return;
    646             }
    647 
    648             Cursor cursor = null;
    649             try {
    650                 cursor = getContentResolver().query(data.getData(),
    651                     NUM_PROJECTION, null, null, null);
    652                 if ((cursor == null) || (!cursor.moveToFirst())) {
    653                     if (DBG) log("onActivityResult: bad contact data, no results found.");
    654                     return;
    655                 }
    656                 mSubMenuVoicemailSettings.onPickActivityResult(cursor.getString(0));
    657                 return;
    658             } finally {
    659                 if (cursor != null) {
    660                     cursor.close();
    661                 }
    662             }
    663         }
    664 
    665         super.onActivityResult(requestCode, resultCode, data);
    666     }
    667 
    668     /**
    669      * Wrapper around showDialog() that will silently do nothing if we're
    670      * not in the foreground.
    671      *
    672      * This is useful here because most of the dialogs we display from
    673      * this class are triggered by asynchronous events (like
    674      * success/failure messages from the telephony layer) and it's
    675      * possible for those events to come in even after the user has gone
    676      * to a different screen.
    677      */
    678     // TODO: this is too brittle: it's still easy to accidentally add new
    679     // code here that calls showDialog() directly (which will result in a
    680     // WindowManager$BadTokenException if called after the activity has
    681     // been stopped.)
    682     //
    683     // It would be cleaner to do the "if (mForeground)" check in one
    684     // central place, maybe by using a single Handler for all asynchronous
    685     // events (and have *that* discard events if we're not in the
    686     // foreground.)
    687     //
    688     // Unfortunately it's not that simple, since we sometimes need to do
    689     // actual work to handle these events whether or not we're in the
    690     // foreground (see the Handler code in mSetOptionComplete for
    691     // example.)
    692     //
    693     // TODO: It's a bit worrisome that we don't do anything in error cases when we're not in the
    694     // foreground. Consider displaying a toast instead.
    695     private void showDialogIfForeground(int id) {
    696         if (mForeground) {
    697             showDialog(id);
    698         }
    699     }
    700 
    701     private void dismissDialogSafely(int id) {
    702         try {
    703             dismissDialog(id);
    704         } catch (IllegalArgumentException e) {
    705             // This is expected in the case where we were in the background
    706             // at the time we would normally have shown the dialog, so we didn't
    707             // show it.
    708         }
    709     }
    710 
    711     /**
    712      * TODO: Refactor to make it easier to understand what's done in the different stages.
    713      */
    714     private void saveVoiceMailAndForwardingNumber(
    715             String key, VoicemailProviderSettings newSettings) {
    716         if (DBG) log("saveVoiceMailAndForwardingNumber: " + newSettings.toString());
    717         mNewVMNumber = newSettings.getVoicemailNumber();
    718         mNewVMNumber = (mNewVMNumber == null) ? "" : mNewVMNumber;
    719         mNewFwdSettings = newSettings.getForwardingSettings();
    720 
    721         // No fwd settings on CDMA.
    722         boolean isCdma = mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA;
    723         if (isCdma) {
    724             if (DBG) log("ignoring forwarding setting since this is CDMA phone");
    725             mNewFwdSettings = VoicemailProviderSettings.NO_FORWARDING;
    726         }
    727 
    728         // Throw a warning if the voicemail is the same and we did not change forwarding.
    729         if (mNewVMNumber.equals(mOldVmNumber)
    730                 && mNewFwdSettings == VoicemailProviderSettings.NO_FORWARDING) {
    731             showDialogIfForeground(VoicemailDialogUtil.VM_NOCHANGE_ERROR_DIALOG);
    732             return;
    733         }
    734 
    735         VoicemailProviderSettingsUtil.save(this, key, newSettings);
    736         mVMChangeCompletedSuccessfully = false;
    737         mFwdChangesRequireRollback = false;
    738         mVMOrFwdSetError = 0;
    739 
    740         // Don't read call forwarding settings if CDMA. Call forwarding is not supported by CDMA.
    741         if (!key.equals(mPreviousVMProviderKey) && !isCdma) {
    742             mReadingSettingsForDefaultProvider =
    743                     mPreviousVMProviderKey.equals(VoicemailProviderListPreference.DEFAULT_KEY);
    744             if (DBG) log("Reading current forwarding settings");
    745             int numSettingsReasons = VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS.length;
    746             mForwardingReadResults = new CallForwardInfo[numSettingsReasons];
    747             for (int i = 0; i < mForwardingReadResults.length; i++) {
    748                 mPhone.getCallForwardingOption(
    749                         VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[i],
    750                         mGetOptionComplete.obtainMessage(EVENT_FORWARDING_GET_COMPLETED, i, 0));
    751             }
    752             showDialogIfForeground(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
    753         } else {
    754             saveVoiceMailAndForwardingNumberStage2();
    755         }
    756 
    757         // Refresh the MWI indicator if it is already showing.
    758         PhoneGlobals.getInstance().refreshMwiIndicator(mSubscriptionInfoHelper.getSubId());
    759     }
    760 
    761     private final Handler mGetOptionComplete = new Handler() {
    762         @Override
    763         public void handleMessage(Message msg) {
    764             AsyncResult result = (AsyncResult) msg.obj;
    765             switch (msg.what) {
    766                 case EVENT_FORWARDING_GET_COMPLETED:
    767                     handleForwardingSettingsReadResult(result, msg.arg1);
    768                     break;
    769             }
    770         }
    771     };
    772 
    773     private void handleForwardingSettingsReadResult(AsyncResult ar, int idx) {
    774         if (DBG) Log.d(LOG_TAG, "handleForwardingSettingsReadResult: " + idx);
    775 
    776         Throwable error = null;
    777         if (ar.exception != null) {
    778             error = ar.exception;
    779             if (DBG) Log.d(LOG_TAG, "FwdRead: ar.exception=" + error.getMessage());
    780         }
    781         if (ar.userObj instanceof Throwable) {
    782             error = (Throwable) ar.userObj;
    783             if (DBG) Log.d(LOG_TAG, "FwdRead: userObj=" + error.getMessage());
    784         }
    785 
    786         // We may have already gotten an error and decided to ignore the other results.
    787         if (mForwardingReadResults == null) {
    788             if (DBG) Log.d(LOG_TAG, "Ignoring fwd reading result: " + idx);
    789             return;
    790         }
    791 
    792         // In case of error ignore other results, show an error dialog
    793         if (error != null) {
    794             if (DBG) Log.d(LOG_TAG, "Error discovered for fwd read : " + idx);
    795             mForwardingReadResults = null;
    796             dismissDialogSafely(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
    797             showDialogIfForeground(VoicemailDialogUtil.FWD_GET_RESPONSE_ERROR_DIALOG);
    798             return;
    799         }
    800 
    801         // Get the forwarding info.
    802         mForwardingReadResults[idx] = CallForwardInfoUtil.getCallForwardInfo(
    803                 (CallForwardInfo[]) ar.result,
    804                 VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[idx]);
    805 
    806         // Check if we got all the results already
    807         boolean done = true;
    808         for (int i = 0; i < mForwardingReadResults.length; i++) {
    809             if (mForwardingReadResults[i] == null) {
    810                 done = false;
    811                 break;
    812             }
    813         }
    814 
    815         if (done) {
    816             if (DBG) Log.d(LOG_TAG, "Done receiving fwd info");
    817             dismissDialogSafely(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
    818 
    819             if (mReadingSettingsForDefaultProvider) {
    820                 VoicemailProviderSettingsUtil.save(mPhone.getContext(),
    821                         VoicemailProviderListPreference.DEFAULT_KEY,
    822                         new VoicemailProviderSettings(mOldVmNumber, mForwardingReadResults));
    823                 mReadingSettingsForDefaultProvider = false;
    824             }
    825             saveVoiceMailAndForwardingNumberStage2();
    826         }
    827     }
    828 
    829     private void resetForwardingChangeState() {
    830         mForwardingChangeResults = new HashMap<Integer, AsyncResult>();
    831         mExpectedChangeResultReasons = new HashSet<Integer>();
    832     }
    833 
    834     // Called after we are done saving the previous forwarding settings if we needed.
    835     private void saveVoiceMailAndForwardingNumberStage2() {
    836         mForwardingChangeResults = null;
    837         mVoicemailChangeResult = null;
    838         if (mNewFwdSettings != VoicemailProviderSettings.NO_FORWARDING) {
    839             resetForwardingChangeState();
    840             for (int i = 0; i < mNewFwdSettings.length; i++) {
    841                 CallForwardInfo fi = mNewFwdSettings[i];
    842                 CallForwardInfo fiForReason =
    843                         CallForwardInfoUtil.infoForReason(mForwardingReadResults, fi.reason);
    844                 final boolean doUpdate = CallForwardInfoUtil.isUpdateRequired(fiForReason, fi);
    845 
    846                 if (doUpdate) {
    847                     if (DBG) log("Setting fwd #: " + i + ": " + fi.toString());
    848                     mExpectedChangeResultReasons.add(i);
    849 
    850                     CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
    851                             mSetOptionComplete.obtainMessage(
    852                                     EVENT_FORWARDING_CHANGED, fi.reason, 0));
    853                 }
    854             }
    855             showDialogIfForeground(VoicemailDialogUtil.VM_FWD_SAVING_DIALOG);
    856         } else {
    857             if (DBG) log("Not touching fwd #");
    858             setVMNumberWithCarrier();
    859         }
    860     }
    861 
    862     private void setVMNumberWithCarrier() {
    863         if (DBG) log("save voicemail #: " + mNewVMNumber);
    864 
    865         mPhone.setVoiceMailNumber(
    866                 mPhone.getVoiceMailAlphaTag().toString(),
    867                 mNewVMNumber,
    868                 Message.obtain(mSetOptionComplete, EVENT_VOICEMAIL_CHANGED));
    869     }
    870 
    871     /**
    872      * Callback to handle option update completions
    873      */
    874     private final Handler mSetOptionComplete = new Handler() {
    875         @Override
    876         public void handleMessage(Message msg) {
    877             AsyncResult result = (AsyncResult) msg.obj;
    878             boolean done = false;
    879             switch (msg.what) {
    880                 case EVENT_VOICEMAIL_CHANGED:
    881                     mVoicemailChangeResult = result;
    882                     mVMChangeCompletedSuccessfully = isVmChangeSuccess();
    883                     done = true;
    884                     break;
    885                 case EVENT_FORWARDING_CHANGED:
    886                     mForwardingChangeResults.put(msg.arg1, result);
    887                     if (result.exception != null) {
    888                         Log.w(LOG_TAG, "Error in setting fwd# " + msg.arg1 + ": " +
    889                                 result.exception.getMessage());
    890                     }
    891                     if (isForwardingCompleted()) {
    892                         if (isFwdChangeSuccess()) {
    893                             if (DBG) log("Overall fwd changes completed ok, starting vm change");
    894                             setVMNumberWithCarrier();
    895                         } else {
    896                             Log.w(LOG_TAG, "Overall fwd changes completed in failure. " +
    897                                     "Check if we need to try rollback for some settings.");
    898                             mFwdChangesRequireRollback = false;
    899                             Iterator<Map.Entry<Integer,AsyncResult>> it =
    900                                 mForwardingChangeResults.entrySet().iterator();
    901                             while (it.hasNext()) {
    902                                 Map.Entry<Integer,AsyncResult> entry = it.next();
    903                                 if (entry.getValue().exception == null) {
    904                                     // If at least one succeeded we have to revert
    905                                     Log.i(LOG_TAG, "Rollback will be required");
    906                                     mFwdChangesRequireRollback = true;
    907                                     break;
    908                                 }
    909                             }
    910                             if (!mFwdChangesRequireRollback) {
    911                                 Log.i(LOG_TAG, "No rollback needed.");
    912                             }
    913                             done = true;
    914                         }
    915                     }
    916                     break;
    917                 default:
    918                     // TODO: should never reach this, may want to throw exception
    919             }
    920 
    921             if (done) {
    922                 if (DBG) log("All VM provider related changes done");
    923                 if (mForwardingChangeResults != null) {
    924                     dismissDialogSafely(VoicemailDialogUtil.VM_FWD_SAVING_DIALOG);
    925                 }
    926                 handleSetVmOrFwdMessage();
    927             }
    928         }
    929     };
    930 
    931     /**
    932      * Callback to handle option revert completions
    933      */
    934     private final Handler mRevertOptionComplete = new Handler() {
    935         @Override
    936         public void handleMessage(Message msg) {
    937             AsyncResult result = (AsyncResult) msg.obj;
    938             switch (msg.what) {
    939                 case EVENT_VOICEMAIL_CHANGED:
    940                     if (DBG) log("VM revert complete msg");
    941                     mVoicemailChangeResult = result;
    942                     break;
    943 
    944                 case EVENT_FORWARDING_CHANGED:
    945                     if (DBG) log("FWD revert complete msg ");
    946                     mForwardingChangeResults.put(msg.arg1, result);
    947                     if (result.exception != null) {
    948                         if (DBG) log("Error in reverting fwd# " + msg.arg1 + ": " +
    949                                 result.exception.getMessage());
    950                     }
    951                     break;
    952 
    953                 default:
    954                     // TODO: should never reach this, may want to throw exception
    955             }
    956 
    957             final boolean done = (!mVMChangeCompletedSuccessfully || mVoicemailChangeResult != null)
    958                     && (!mFwdChangesRequireRollback || isForwardingCompleted());
    959             if (done) {
    960                 if (DBG) log("All VM reverts done");
    961                 dismissDialogSafely(VoicemailDialogUtil.VM_REVERTING_DIALOG);
    962                 onRevertDone();
    963             }
    964         }
    965     };
    966 
    967     /**
    968      * Return true if there is a change result for every reason for which we expect a result.
    969      */
    970     private boolean isForwardingCompleted() {
    971         if (mForwardingChangeResults == null) {
    972             return true;
    973         }
    974 
    975         for (Integer reason : mExpectedChangeResultReasons) {
    976             if (mForwardingChangeResults.get(reason) == null) {
    977                 return false;
    978             }
    979         }
    980 
    981         return true;
    982     }
    983 
    984     private boolean isFwdChangeSuccess() {
    985         if (mForwardingChangeResults == null) {
    986             return true;
    987         }
    988 
    989         for (AsyncResult result : mForwardingChangeResults.values()) {
    990             Throwable exception = result.exception;
    991             if (exception != null) {
    992                 String msg = exception.getMessage();
    993                 msg = (msg != null) ? msg : "";
    994                 Log.w(LOG_TAG, "Failed to change forwarding setting. Reason: " + msg);
    995                 return false;
    996             }
    997         }
    998         return true;
    999     }
   1000 
   1001     private boolean isVmChangeSuccess() {
   1002         if (mVoicemailChangeResult.exception != null) {
   1003             String msg = mVoicemailChangeResult.exception.getMessage();
   1004             msg = (msg != null) ? msg : "";
   1005             Log.w(LOG_TAG, "Failed to change voicemail. Reason: " + msg);
   1006             return false;
   1007         }
   1008         return true;
   1009     }
   1010 
   1011     private void handleSetVmOrFwdMessage() {
   1012         if (DBG) log("handleSetVMMessage: set VM request complete");
   1013 
   1014         if (!isFwdChangeSuccess()) {
   1015             handleVmOrFwdSetError(VoicemailDialogUtil.FWD_SET_RESPONSE_ERROR_DIALOG);
   1016         } else if (!isVmChangeSuccess()) {
   1017             handleVmOrFwdSetError(VoicemailDialogUtil.VM_RESPONSE_ERROR_DIALOG);
   1018         } else {
   1019             handleVmAndFwdSetSuccess(VoicemailDialogUtil.VM_CONFIRM_DIALOG);
   1020         }
   1021     }
   1022 
   1023     /**
   1024      * Called when Voicemail Provider or its forwarding settings failed. Rolls back partly made
   1025      * changes to those settings and show "failure" dialog.
   1026      *
   1027      * @param dialogId ID of the dialog to show for the specific error case. Either
   1028      *     {@link #FWD_SET_RESPONSE_ERROR_DIALOG} or {@link #VM_RESPONSE_ERROR_DIALOG}
   1029      */
   1030     private void handleVmOrFwdSetError(int dialogId) {
   1031         if (mChangingVMorFwdDueToProviderChange) {
   1032             mVMOrFwdSetError = dialogId;
   1033             mChangingVMorFwdDueToProviderChange = false;
   1034             switchToPreviousVoicemailProvider();
   1035             return;
   1036         }
   1037         mChangingVMorFwdDueToProviderChange = false;
   1038         showDialogIfForeground(dialogId);
   1039         updateVoiceNumberField();
   1040     }
   1041 
   1042     /**
   1043      * Called when Voicemail Provider and its forwarding settings were successfully finished.
   1044      * This updates a bunch of variables and show "success" dialog.
   1045      */
   1046     private void handleVmAndFwdSetSuccess(int dialogId) {
   1047         if (DBG) log("handleVmAndFwdSetSuccess: key is " + mVoicemailProviders.getKey());
   1048 
   1049         mPreviousVMProviderKey = mVoicemailProviders.getKey();
   1050         mChangingVMorFwdDueToProviderChange = false;
   1051         showDialogIfForeground(dialogId);
   1052         updateVoiceNumberField();
   1053     }
   1054 
   1055     /**
   1056      * Update the voicemail number from what we've recorded on the sim.
   1057      */
   1058     private void updateVoiceNumberField() {
   1059         if (DBG) log("updateVoiceNumberField()");
   1060 
   1061         mOldVmNumber = mPhone.getVoiceMailNumber();
   1062         if (TextUtils.isEmpty(mOldVmNumber)) {
   1063             mSubMenuVoicemailSettings.setPhoneNumber("");
   1064             mSubMenuVoicemailSettings.setSummary(getString(R.string.voicemail_number_not_set));
   1065         } else {
   1066             mSubMenuVoicemailSettings.setPhoneNumber(mOldVmNumber);
   1067             mSubMenuVoicemailSettings.setSummary(mOldVmNumber);
   1068         }
   1069     }
   1070 
   1071     /*
   1072      * Helper Methods for Activity class.
   1073      * The initial query commands are split into two pieces now
   1074      * for individual expansion.  This combined with the ability
   1075      * to cancel queries allows for a much better user experience,
   1076      * and also ensures that the user only waits to update the
   1077      * data that is relevant.
   1078      */
   1079 
   1080     @Override
   1081     protected void onPrepareDialog(int id, Dialog dialog) {
   1082         super.onPrepareDialog(id, dialog);
   1083         mCurrentDialogId = id;
   1084     }
   1085 
   1086     // dialog creation method, called by showDialog()
   1087     @Override
   1088     protected Dialog onCreateDialog(int dialogId) {
   1089         return VoicemailDialogUtil.getDialog(this, dialogId);
   1090     }
   1091 
   1092     // This is a method implemented for DialogInterface.OnClickListener.
   1093     // Used with the error dialog to close the app, voicemail dialog to just dismiss.
   1094     // Close button is mapped to BUTTON_POSITIVE for the errors that close the activity,
   1095     // while those that are mapped to BUTTON_NEUTRAL only move the preference focus.
   1096     public void onClick(DialogInterface dialog, int which) {
   1097         if (DBG) log("onClick: button clicked is " + which);
   1098 
   1099         dialog.dismiss();
   1100         switch (which){
   1101             case DialogInterface.BUTTON_NEGATIVE:
   1102                 if (mCurrentDialogId == VoicemailDialogUtil.FWD_GET_RESPONSE_ERROR_DIALOG) {
   1103                     // We failed to get current forwarding settings and the user
   1104                     // does not wish to continue.
   1105                     switchToPreviousVoicemailProvider();
   1106                 }
   1107                 break;
   1108             case DialogInterface.BUTTON_POSITIVE:
   1109                 if (mCurrentDialogId == VoicemailDialogUtil.FWD_GET_RESPONSE_ERROR_DIALOG) {
   1110                     // We failed to get current forwarding settings but the user
   1111                     // wishes to continue changing settings to the new vm provider
   1112                     saveVoiceMailAndForwardingNumberStage2();
   1113                 } else {
   1114                     finish();
   1115                 }
   1116                 return;
   1117             default:
   1118                 // just let the dialog close and go back to the input
   1119         }
   1120 
   1121         // In all dialogs, all buttons except BUTTON_POSITIVE lead to the end of user interaction
   1122         // with settings UI. If we were called to explicitly configure voice mail then
   1123         // we finish the settings activity here to come back to whatever the user was doing.
   1124         if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) {
   1125             finish();
   1126         }
   1127     }
   1128 
   1129     /*
   1130      * Activity class methods
   1131      */
   1132 
   1133     @Override
   1134     protected void onCreate(Bundle icicle) {
   1135         super.onCreate(icicle);
   1136         if (DBG) log("onCreate: Intent is " + getIntent());
   1137 
   1138         // Make sure we are running as the primary user.
   1139         if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
   1140             Toast.makeText(this, R.string.call_settings_primary_user_only,
   1141                     Toast.LENGTH_SHORT).show();
   1142             finish();
   1143             return;
   1144         }
   1145 
   1146         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
   1147 
   1148         // Show the voicemail preference in onResume if the calling intent specifies the
   1149         // ACTION_ADD_VOICEMAIL action.
   1150         mShowVoicemailPreference = (icicle == null) &&
   1151                 TextUtils.equals(getIntent().getAction(), ACTION_ADD_VOICEMAIL);
   1152         mSetupVoicemail = mShowVoicemailPreference &&
   1153                 getIntent().getBooleanExtra(SETUP_VOICEMAIL_EXTRA, false);
   1154 
   1155         mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
   1156         mSubscriptionInfoHelper.setActionBarTitle(
   1157                 getActionBar(), getResources(), R.string.call_settings_with_label);
   1158         mPhone = mSubscriptionInfoHelper.getPhone();
   1159     }
   1160 
   1161     @Override
   1162     protected void onResume() {
   1163         super.onResume();
   1164         mForeground = true;
   1165 
   1166         PreferenceScreen preferenceScreen = getPreferenceScreen();
   1167         if (preferenceScreen != null) {
   1168             preferenceScreen.removeAll();
   1169         }
   1170 
   1171         addPreferencesFromResource(R.xml.call_feature_setting);
   1172 
   1173         TelecomManager telecomManager = TelecomManager.from(this);
   1174         TelephonyManager telephonyManager =
   1175                 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
   1176 
   1177         Preference phoneAccountSettingsPreference = findPreference(PHONE_ACCOUNT_SETTINGS_KEY);
   1178         if (telephonyManager.isMultiSimEnabled() || (telecomManager.getSimCallManagers().isEmpty()
   1179                     && !SipUtil.isVoipSupported(mPhone.getContext()))) {
   1180             getPreferenceScreen().removePreference(phoneAccountSettingsPreference);
   1181         }
   1182 
   1183         PreferenceScreen prefSet = getPreferenceScreen();
   1184         mSubMenuVoicemailSettings = (EditPhoneNumberPreference) findPreference(BUTTON_VOICEMAIL_KEY);
   1185         mSubMenuVoicemailSettings.setParentActivity(this, VOICEMAIL_PREF_ID, this);
   1186         mSubMenuVoicemailSettings.setDialogOnClosedListener(this);
   1187         mSubMenuVoicemailSettings.setDialogTitle(R.string.voicemail_settings_number_label);
   1188 
   1189         mButtonDTMF = (ListPreference) findPreference(BUTTON_DTMF_KEY);
   1190         mButtonAutoRetry = (CheckBoxPreference) findPreference(BUTTON_RETRY_KEY);
   1191         mButtonHAC = (CheckBoxPreference) findPreference(BUTTON_HAC_KEY);
   1192         mButtonTTY = (TtyModeListPreference) findPreference(
   1193                 getResources().getString(R.string.tty_mode_key));
   1194 
   1195         mVoicemailProviders = (VoicemailProviderListPreference) findPreference(
   1196                 BUTTON_VOICEMAIL_PROVIDER_KEY);
   1197         mVoicemailProviders.init(mPhone, getIntent());
   1198         mVoicemailProviders.setOnPreferenceChangeListener(this);
   1199         mPreviousVMProviderKey = mVoicemailProviders.getValue();
   1200 
   1201         mVoicemailSettingsScreen =
   1202                 (PreferenceScreen) findPreference(VOICEMAIL_SETTING_SCREEN_PREF_KEY);
   1203         mVoicemailSettings = (PreferenceScreen) findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
   1204 
   1205         mVoicemailNotificationRingtone = (VoicemailRingtonePreference) findPreference(
   1206                 getResources().getString(R.string.voicemail_notification_ringtone_key));
   1207         mVoicemailNotificationRingtone.init(mPhone);
   1208 
   1209         mVoicemailNotificationVibrate = (CheckBoxPreference) findPreference(
   1210                 getResources().getString(R.string.voicemail_notification_vibrate_key));
   1211         mVoicemailNotificationVibrate.setOnPreferenceChangeListener(this);
   1212 
   1213         updateVMPreferenceWidgets(mVoicemailProviders.getValue());
   1214 
   1215         mEnableVideoCalling = (CheckBoxPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
   1216 
   1217         if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
   1218             mButtonDTMF.setOnPreferenceChangeListener(this);
   1219             int dtmf = Settings.System.getInt(getContentResolver(),
   1220                     Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, Constants.DTMF_TONE_TYPE_NORMAL);
   1221             mButtonDTMF.setValueIndex(dtmf);
   1222         } else {
   1223             prefSet.removePreference(mButtonDTMF);
   1224             mButtonDTMF = null;
   1225         }
   1226 
   1227         if (getResources().getBoolean(R.bool.auto_retry_enabled)) {
   1228             mButtonAutoRetry.setOnPreferenceChangeListener(this);
   1229             int autoretry = Settings.Global.getInt(
   1230                     getContentResolver(), Settings.Global.CALL_AUTO_RETRY, 0);
   1231             mButtonAutoRetry.setChecked(autoretry != 0);
   1232         } else {
   1233             prefSet.removePreference(mButtonAutoRetry);
   1234             mButtonAutoRetry = null;
   1235         }
   1236 
   1237         if (getResources().getBoolean(R.bool.hac_enabled)) {
   1238             mButtonHAC.setOnPreferenceChangeListener(this);
   1239             int hac = Settings.System.getInt(getContentResolver(), Settings.System.HEARING_AID, 0);
   1240             mButtonHAC.setChecked(hac != 0);
   1241         } else {
   1242             prefSet.removePreference(mButtonHAC);
   1243             mButtonHAC = null;
   1244         }
   1245 
   1246         if (!telephonyManager.isMultiSimEnabled() && telecomManager.isTtySupported()) {
   1247             mButtonTTY.init();
   1248         } else {
   1249             prefSet.removePreference(mButtonTTY);
   1250             mButtonTTY = null;
   1251         }
   1252 
   1253         if (!getResources().getBoolean(R.bool.world_phone)) {
   1254             Preference cdmaOptions = prefSet.findPreference(BUTTON_CDMA_OPTIONS);
   1255             prefSet.removePreference(cdmaOptions);
   1256 
   1257             // TODO: Support MSIM for this preference option.
   1258             Preference gsmOptions = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS);
   1259             prefSet.removePreference(gsmOptions);
   1260 
   1261             int phoneType = mPhone.getPhoneType();
   1262             Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
   1263             boolean shouldHideCarrierSettings = Settings.Global.getInt(
   1264                     getContentResolver(), Settings.Global.HIDE_CARRIER_NETWORK_SETTINGS, 0) == 1;
   1265             if (shouldHideCarrierSettings) {
   1266                 prefSet.removePreference(fdnButton);
   1267                 if (mButtonDTMF != null) {
   1268                     prefSet.removePreference(mButtonDTMF);
   1269                 }
   1270             } else {
   1271                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
   1272                     prefSet.removePreference(fdnButton);
   1273 
   1274                     if (!getResources().getBoolean(R.bool.config_voice_privacy_disable)) {
   1275                         addPreferencesFromResource(R.xml.cdma_call_privacy);
   1276                     }
   1277                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
   1278                     fdnButton.setIntent(mSubscriptionInfoHelper.getIntent(FdnSetting.class));
   1279 
   1280                     if (getResources().getBoolean(R.bool.config_additional_call_setting)) {
   1281                         addPreferencesFromResource(R.xml.gsm_umts_call_options);
   1282 
   1283                         Preference callForwardingPref = prefSet.findPreference(CALL_FORWARDING_KEY);
   1284                         callForwardingPref.setIntent(mSubscriptionInfoHelper.getIntent(
   1285                                 GsmUmtsCallForwardOptions.class));
   1286 
   1287                         Preference additionalGsmSettingsPref =
   1288                                 prefSet.findPreference(ADDITIONAL_GSM_SETTINGS_KEY);
   1289                         additionalGsmSettingsPref.setIntent(mSubscriptionInfoHelper.getIntent(
   1290                                 GsmUmtsAdditionalCallOptions.class));
   1291                     }
   1292                 } else {
   1293                     throw new IllegalStateException("Unexpected phone type: " + phoneType);
   1294                 }
   1295             }
   1296         }
   1297 
   1298         // check the intent that started this activity and pop up the voicemail
   1299         // dialog if we've been asked to.
   1300         // If we have at least one non default VM provider registered then bring up
   1301         // the selection for the VM provider, otherwise bring up a VM number dialog.
   1302         // We only bring up the dialog the first time we are called (not after orientation change)
   1303         if (mShowVoicemailPreference) {
   1304             if (DBG) log("ACTION_ADD_VOICEMAIL Intent is thrown");
   1305             if (mSetupVoicemail) {
   1306                 simulatePreferenceClick(mVoicemailSettingsScreen);
   1307                 mSetupVoicemail = false;
   1308             } else if (mVoicemailProviders.hasMoreThanOneVoicemailProvider()) {
   1309                 if (DBG) log("Voicemail data has more than one provider.");
   1310                 simulatePreferenceClick(mVoicemailProviders);
   1311             } else {
   1312                 onPreferenceChange(mVoicemailProviders, VoicemailProviderListPreference.DEFAULT_KEY);
   1313                 mVoicemailProviders.setValue(VoicemailProviderListPreference.DEFAULT_KEY);
   1314             }
   1315             mShowVoicemailPreference = false;
   1316         }
   1317 
   1318         updateVoiceNumberField();
   1319         mVMProviderSettingsForced = false;
   1320 
   1321         mVoicemailNotificationVibrate.setChecked(
   1322                 VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone));
   1323 
   1324         if (ImsManager.isVtEnabledByPlatform(mPhone.getContext()) && ENABLE_VT_FLAG) {
   1325             boolean currentValue =
   1326                     ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mPhone.getContext())
   1327                     ? PhoneGlobals.getInstance().phoneMgr.isVideoCallingEnabled() : false;
   1328             mEnableVideoCalling.setChecked(currentValue);
   1329             mEnableVideoCalling.setOnPreferenceChangeListener(this);
   1330         } else {
   1331             prefSet.removePreference(mEnableVideoCalling);
   1332         }
   1333 
   1334         if (ImsManager.isVolteEnabledByPlatform(this) &&
   1335                 !mPhone.getContext().getResources().getBoolean(
   1336                         com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
   1337             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
   1338             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
   1339         }
   1340     }
   1341 
   1342     @Override
   1343     protected void onNewIntent(Intent newIntent) {
   1344         setIntent(newIntent);
   1345 
   1346         mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
   1347         mSubscriptionInfoHelper.setActionBarTitle(
   1348                 getActionBar(), getResources(), R.string.call_settings_with_label);
   1349         mPhone = mSubscriptionInfoHelper.getPhone();
   1350     }
   1351 
   1352     private static void log(String msg) {
   1353         Log.d(LOG_TAG, msg);
   1354     }
   1355 
   1356     /**
   1357      * Updates the look of the VM preference widgets based on current VM provider settings.
   1358      * Note that the provider name is loaded fxrorm the found activity via loadLabel in
   1359      * {@link VoicemailProviderListPreference#initVoiceMailProviders()} in order for it to be
   1360      * localizable.
   1361      */
   1362     private void updateVMPreferenceWidgets(String currentProviderSetting) {
   1363         final String key = currentProviderSetting;
   1364         final VoicemailProvider provider = mVoicemailProviders.getVoicemailProvider(key);
   1365 
   1366         /* This is the case when we are coming up on a freshly wiped phone and there is no
   1367          persisted value for the list preference mVoicemailProviders.
   1368          In this case we want to show the UI asking the user to select a voicemail provider as
   1369          opposed to silently falling back to default one. */
   1370         if (provider == null) {
   1371             if (DBG) log("updateVMPreferenceWidget: key: " + key + " -> null.");
   1372 
   1373             mVoicemailProviders.setSummary(getString(R.string.sum_voicemail_choose_provider));
   1374             mVoicemailSettings.setEnabled(false);
   1375             mVoicemailSettings.setIntent(null);
   1376             mVoicemailNotificationVibrate.setEnabled(false);
   1377         } else {
   1378             if (DBG) log("updateVMPreferenceWidget: key: " + key + " -> " + provider.toString());
   1379 
   1380             final String providerName = provider.name;
   1381             mVoicemailProviders.setSummary(providerName);
   1382             mVoicemailSettings.setEnabled(true);
   1383             mVoicemailSettings.setIntent(provider.intent);
   1384             mVoicemailNotificationVibrate.setEnabled(true);
   1385         }
   1386     }
   1387 
   1388 
   1389     /**
   1390      * Simulates user clicking on a passed preference.
   1391      * Usually needed when the preference is a dialog preference and we want to invoke
   1392      * a dialog for this preference programmatically.
   1393      * TODO: figure out if there is a cleaner way to cause preference dlg to come up
   1394      */
   1395     private void simulatePreferenceClick(Preference preference) {
   1396         // Go through settings until we find our setting
   1397         // and then simulate a click on it to bring up the dialog
   1398         final ListAdapter adapter = getPreferenceScreen().getRootAdapter();
   1399         for (int idx = 0; idx < adapter.getCount(); idx++) {
   1400             if (adapter.getItem(idx) == preference) {
   1401                 getPreferenceScreen().onItemClick(this.getListView(),
   1402                         null, idx, adapter.getItemId(idx));
   1403                 break;
   1404             }
   1405         }
   1406     }
   1407 
   1408     @Override
   1409     public boolean onOptionsItemSelected(MenuItem item) {
   1410         final int itemId = item.getItemId();
   1411         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
   1412             onBackPressed();
   1413             return true;
   1414         }
   1415         return super.onOptionsItemSelected(item);
   1416     }
   1417 
   1418     /**
   1419      * Finish current Activity and go up to the top level Settings ({@link CallFeaturesSetting}).
   1420      * This is useful for implementing "HomeAsUp" capability for second-level Settings.
   1421      */
   1422     public static void goUpToTopLevelSetting(
   1423             Activity activity, SubscriptionInfoHelper subscriptionInfoHelper) {
   1424         Intent intent = subscriptionInfoHelper.getIntent(CallFeaturesSetting.class);
   1425         intent.setAction(Intent.ACTION_MAIN);
   1426         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   1427         activity.startActivity(intent);
   1428         activity.finish();
   1429     }
   1430 }
   1431