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