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