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
onPause()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
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)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
onPreferenceChange(Preference preference, Object objValue)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
onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked)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
onGetDefaultNumber(EditPhoneNumberPreference preference)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 
switchToPreviousVoicemailProvider()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 
onRevertDone()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
onActivityResult(int requestCode, int resultCode, Intent data)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.
showDialogIfForeground(int id)695     private void showDialogIfForeground(int id) {
696         if (mForeground) {
697             showDialog(id);
698         }
699     }
700 
dismissDialogSafely(int id)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      */
saveVoiceMailAndForwardingNumber( String key, VoicemailProviderSettings newSettings)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 
handleForwardingSettingsReadResult(AsyncResult ar, int idx)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 
resetForwardingChangeState()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.
saveVoiceMailAndForwardingNumberStage2()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 
setVMNumberWithCarrier()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      */
isForwardingCompleted()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 
isFwdChangeSuccess()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 
isVmChangeSuccess()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 
handleSetVmOrFwdMessage()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      */
handleVmOrFwdSetError(int dialogId)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      */
handleVmAndFwdSetSuccess(int dialogId)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      */
updateVoiceNumberField()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
onPrepareDialog(int id, Dialog dialog)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
onCreateDialog(int dialogId)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.
onClick(DialogInterface dialog, int which)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
onCreate(Bundle icicle)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
onResume()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
onNewIntent(Intent newIntent)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 
log(String msg)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      */
updateVMPreferenceWidgets(String currentProviderSetting)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      */
simulatePreferenceClick(Preference preference)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
onOptionsItemSelected(MenuItem item)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      */
goUpToTopLevelSetting( Activity activity, SubscriptionInfoHelper subscriptionInfoHelper)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