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.ContentResolver; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.SharedPreferences; 29 import android.content.SharedPreferences.Editor; 30 import android.content.pm.ActivityInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.database.Cursor; 34 import android.database.sqlite.SQLiteException; 35 import android.media.AudioManager; 36 import android.media.RingtoneManager; 37 import android.net.Uri; 38 import android.net.sip.SipManager; 39 import android.os.AsyncResult; 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.os.Message; 43 import android.os.UserHandle; 44 import android.os.Vibrator; 45 import android.preference.CheckBoxPreference; 46 import android.preference.ListPreference; 47 import android.preference.Preference; 48 import android.preference.PreferenceActivity; 49 import android.preference.PreferenceGroup; 50 import android.preference.PreferenceManager; 51 import android.preference.PreferenceScreen; 52 import android.provider.ContactsContract.CommonDataKinds; 53 import android.provider.MediaStore; 54 import android.provider.Settings; 55 import android.provider.Settings.SettingNotFoundException; 56 import android.telephony.PhoneNumberUtils; 57 import android.text.Spannable; 58 import android.text.SpannableString; 59 import android.text.TextUtils; 60 import android.text.style.TypefaceSpan; 61 import android.util.Log; 62 import android.view.MenuItem; 63 import android.view.WindowManager; 64 import android.widget.ListAdapter; 65 66 import com.android.internal.telephony.CallForwardInfo; 67 import com.android.internal.telephony.CommandsInterface; 68 import com.android.internal.telephony.Phone; 69 import com.android.internal.telephony.PhoneConstants; 70 import com.android.internal.telephony.cdma.TtyIntent; 71 import com.android.phone.sip.SipSharedPreferences; 72 73 import java.util.Collection; 74 import java.util.HashMap; 75 import java.util.HashSet; 76 import java.util.Iterator; 77 import java.util.List; 78 import java.util.Map; 79 80 /** 81 * Top level "Call settings" UI; see res/xml/call_feature_setting.xml 82 * 83 * This preference screen is the root of the "Call settings" hierarchy 84 * available from the Phone app; the settings here let you control various 85 * features related to phone calls (including voicemail settings, SIP 86 * settings, the "Respond via SMS" feature, and others.) It's used only 87 * on voice-capable phone devices. 88 * 89 * Note that this activity is part of the package com.android.phone, even 90 * though you reach it from the "Phone" app (i.e. DialtactsActivity) which 91 * is from the package com.android.contacts. 92 * 93 * For the "Mobile network settings" screen under the main Settings app, 94 * See {@link MobileNetworkSettings}. 95 * 96 * @see com.android.phone.MobileNetworkSettings 97 */ 98 public class CallFeaturesSetting extends PreferenceActivity 99 implements DialogInterface.OnClickListener, 100 Preference.OnPreferenceChangeListener, 101 EditPhoneNumberPreference.OnDialogClosedListener, 102 EditPhoneNumberPreference.GetDefaultNumberListener{ 103 private static final String LOG_TAG = "CallFeaturesSetting"; 104 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); 105 106 /** 107 * Intent action to bring up Voicemail Provider settings. 108 * 109 * @see #IGNORE_PROVIDER_EXTRA 110 */ 111 public static final String ACTION_ADD_VOICEMAIL = 112 "com.android.phone.CallFeaturesSetting.ADD_VOICEMAIL"; 113 // intent action sent by this activity to a voice mail provider 114 // to trigger its configuration UI 115 public static final String ACTION_CONFIGURE_VOICEMAIL = 116 "com.android.phone.CallFeaturesSetting.CONFIGURE_VOICEMAIL"; 117 // Extra put in the return from VM provider config containing voicemail number to set 118 public static final String VM_NUMBER_EXTRA = "com.android.phone.VoicemailNumber"; 119 // Extra put in the return from VM provider config containing call forwarding number to set 120 public static final String FWD_NUMBER_EXTRA = "com.android.phone.ForwardingNumber"; 121 // Extra put in the return from VM provider config containing call forwarding number to set 122 public static final String FWD_NUMBER_TIME_EXTRA = "com.android.phone.ForwardingNumberTime"; 123 // If the VM provider returns non null value in this extra we will force the user to 124 // choose another VM provider 125 public static final String SIGNOUT_EXTRA = "com.android.phone.Signout"; 126 //Information about logical "up" Activity 127 private static final String UP_ACTIVITY_PACKAGE = "com.android.dialer"; 128 private static final String UP_ACTIVITY_CLASS = 129 "com.android.dialer.DialtactsActivity"; 130 131 // Used to tell the saving logic to leave forwarding number as is 132 public static final CallForwardInfo[] FWD_SETTINGS_DONT_TOUCH = null; 133 // Suffix appended to provider key for storing vm number 134 public static final String VM_NUMBER_TAG = "#VMNumber"; 135 // Suffix appended to provider key for storing forwarding settings 136 public static final String FWD_SETTINGS_TAG = "#FWDSettings"; 137 // Suffix appended to forward settings key for storing length of settings array 138 public static final String FWD_SETTINGS_LENGTH_TAG = "#Length"; 139 // Suffix appended to forward settings key for storing an individual setting 140 public static final String FWD_SETTING_TAG = "#Setting"; 141 // Suffixes appended to forward setting key for storing an individual setting properties 142 public static final String FWD_SETTING_STATUS = "#Status"; 143 public static final String FWD_SETTING_REASON = "#Reason"; 144 public static final String FWD_SETTING_NUMBER = "#Number"; 145 public static final String FWD_SETTING_TIME = "#Time"; 146 147 // Key identifying the default vocie mail provider 148 public static final String DEFAULT_VM_PROVIDER_KEY = ""; 149 150 /** 151 * String Extra put into ACTION_ADD_VOICEMAIL call to indicate which provider should be hidden 152 * in the list of providers presented to the user. This allows a provider which is being 153 * disabled (e.g. GV user logging out) to force the user to pick some other provider. 154 */ 155 public static final String IGNORE_PROVIDER_EXTRA = "com.android.phone.ProviderToIgnore"; 156 157 // string constants 158 private static final String NUM_PROJECTION[] = {CommonDataKinds.Phone.NUMBER}; 159 160 // String keys for preference lookup 161 // TODO: Naming these "BUTTON_*" is confusing since they're not actually buttons(!) 162 private static final String BUTTON_VOICEMAIL_KEY = "button_voicemail_key"; 163 private static final String BUTTON_VOICEMAIL_PROVIDER_KEY = "button_voicemail_provider_key"; 164 private static final String BUTTON_VOICEMAIL_SETTING_KEY = "button_voicemail_setting_key"; 165 // New preference key for voicemail notification vibration 166 /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY = 167 "button_voicemail_notification_vibrate_key"; 168 // Old preference key for voicemail notification vibration. Used for migration to the new 169 // preference key only. 170 /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY = 171 "button_voicemail_notification_vibrate_when_key"; 172 /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_RINGTONE_KEY = 173 "button_voicemail_notification_ringtone_key"; 174 private static final String BUTTON_FDN_KEY = "button_fdn_key"; 175 private static final String BUTTON_RESPOND_VIA_SMS_KEY = "button_respond_via_sms_key"; 176 177 private static final String BUTTON_RINGTONE_KEY = "button_ringtone_key"; 178 private static final String BUTTON_VIBRATE_ON_RING = "button_vibrate_on_ring"; 179 private static final String BUTTON_PLAY_DTMF_TONE = "button_play_dtmf_tone"; 180 private static final String BUTTON_DTMF_KEY = "button_dtmf_settings"; 181 private static final String BUTTON_RETRY_KEY = "button_auto_retry_key"; 182 private static final String BUTTON_TTY_KEY = "button_tty_mode_key"; 183 private static final String BUTTON_HAC_KEY = "button_hac_key"; 184 185 private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key"; 186 private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key"; 187 188 private static final String VM_NUMBERS_SHARED_PREFERENCES_NAME = "vm_numbers"; 189 190 private static final String BUTTON_SIP_CALL_OPTIONS = 191 "sip_call_options_key"; 192 private static final String BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY = 193 "sip_call_options_wifi_only_key"; 194 private static final String SIP_SETTINGS_CATEGORY_KEY = 195 "sip_settings_category_key"; 196 197 private Intent mContactListIntent; 198 199 /** Event for Async voicemail change call */ 200 private static final int EVENT_VOICEMAIL_CHANGED = 500; 201 private static final int EVENT_FORWARDING_CHANGED = 501; 202 private static final int EVENT_FORWARDING_GET_COMPLETED = 502; 203 204 private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1; 205 private static final int MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY = 2; 206 207 // preferred TTY mode 208 // Phone.TTY_MODE_xxx 209 static final int preferredTtyMode = Phone.TTY_MODE_OFF; 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 { 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 */ 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 325 public VoiceMailProviderSettings(String voicemailNumber, CallForwardInfo[] infos) { 326 this.voicemailNumber = voicemailNumber; 327 this.forwardingSettings = infos; 328 } 329 330 @Override 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 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 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 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 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 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 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 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 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 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 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 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 = null; 811 try { 812 cursor = getContentResolver().query(data.getData(), 813 NUM_PROJECTION, null, null, null); 814 if ((cursor == null) || (!cursor.moveToFirst())) { 815 if (DBG) log("onActivityResult: bad contact data, no results found."); 816 return; 817 } 818 mSubMenuVoicemailSettings.onPickActivityResult(cursor.getString(0)); 819 return; 820 } finally { 821 if (cursor != null) { 822 cursor.close(); 823 } 824 } 825 } 826 827 super.onActivityResult(requestCode, resultCode, data); 828 } 829 830 // Voicemail button logic 831 private void handleVMBtnClickRequest() { 832 // normally called on the dialog close. 833 834 // Since we're stripping the formatting out on the getPhoneNumber() 835 // call now, we won't need to do so here anymore. 836 837 saveVoiceMailAndForwardingNumber( 838 getCurrentVoicemailProviderKey(), 839 new VoiceMailProviderSettings(mSubMenuVoicemailSettings.getPhoneNumber(), 840 FWD_SETTINGS_DONT_TOUCH)); 841 } 842 843 844 /** 845 * Wrapper around showDialog() that will silently do nothing if we're 846 * not in the foreground. 847 * 848 * This is useful here because most of the dialogs we display from 849 * this class are triggered by asynchronous events (like 850 * success/failure messages from the telephony layer) and it's 851 * possible for those events to come in even after the user has gone 852 * to a different screen. 853 */ 854 // TODO: this is too brittle: it's still easy to accidentally add new 855 // code here that calls showDialog() directly (which will result in a 856 // WindowManager$BadTokenException if called after the activity has 857 // been stopped.) 858 // 859 // It would be cleaner to do the "if (mForeground)" check in one 860 // central place, maybe by using a single Handler for all asynchronous 861 // events (and have *that* discard events if we're not in the 862 // foreground.) 863 // 864 // Unfortunately it's not that simple, since we sometimes need to do 865 // actual work to handle these events whether or not we're in the 866 // foreground (see the Handler code in mSetOptionComplete for 867 // example.) 868 private void showDialogIfForeground(int id) { 869 if (mForeground) { 870 showDialog(id); 871 } 872 } 873 874 private void dismissDialogSafely(int id) { 875 try { 876 dismissDialog(id); 877 } catch (IllegalArgumentException e) { 878 // This is expected in the case where we were in the background 879 // at the time we would normally have shown the dialog, so we didn't 880 // show it. 881 } 882 } 883 884 private void saveVoiceMailAndForwardingNumber(String key, 885 VoiceMailProviderSettings newSettings) { 886 if (DBG) log("saveVoiceMailAndForwardingNumber: " + newSettings.toString()); 887 mNewVMNumber = newSettings.voicemailNumber; 888 // empty vm number == clearing the vm number ? 889 if (mNewVMNumber == null) { 890 mNewVMNumber = ""; 891 } 892 893 mNewFwdSettings = newSettings.forwardingSettings; 894 if (DBG) log("newFwdNumber " + 895 String.valueOf((mNewFwdSettings != null ? mNewFwdSettings.length : 0)) 896 + " settings"); 897 898 // No fwd settings on CDMA 899 if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 900 if (DBG) log("ignoring forwarding setting since this is CDMA phone"); 901 mNewFwdSettings = FWD_SETTINGS_DONT_TOUCH; 902 } 903 904 //throw a warning if the vm is the same and we do not touch forwarding. 905 if (mNewVMNumber.equals(mOldVmNumber) && mNewFwdSettings == FWD_SETTINGS_DONT_TOUCH) { 906 showVMDialog(MSG_VM_NOCHANGE); 907 return; 908 } 909 910 maybeSaveSettingsForVoicemailProvider(key, newSettings); 911 mVMChangeCompletedSuccessfully = false; 912 mFwdChangesRequireRollback = false; 913 mVMOrFwdSetError = 0; 914 if (!key.equals(mPreviousVMProviderKey)) { 915 mReadingSettingsForDefaultProvider = 916 mPreviousVMProviderKey.equals(DEFAULT_VM_PROVIDER_KEY); 917 if (DBG) log("Reading current forwarding settings"); 918 mForwardingReadResults = new CallForwardInfo[FORWARDING_SETTINGS_REASONS.length]; 919 for (int i = 0; i < FORWARDING_SETTINGS_REASONS.length; i++) { 920 mForwardingReadResults[i] = null; 921 mPhone.getCallForwardingOption(FORWARDING_SETTINGS_REASONS[i], 922 mGetOptionComplete.obtainMessage(EVENT_FORWARDING_GET_COMPLETED, i, 0)); 923 } 924 showDialogIfForeground(VOICEMAIL_FWD_READING_DIALOG); 925 } else { 926 saveVoiceMailAndForwardingNumberStage2(); 927 } 928 } 929 930 private final Handler mGetOptionComplete = new Handler() { 931 @Override 932 public void handleMessage(Message msg) { 933 AsyncResult result = (AsyncResult) msg.obj; 934 switch (msg.what) { 935 case EVENT_FORWARDING_GET_COMPLETED: 936 handleForwardingSettingsReadResult(result, msg.arg1); 937 break; 938 } 939 } 940 }; 941 942 private void handleForwardingSettingsReadResult(AsyncResult ar, int idx) { 943 if (DBG) Log.d(LOG_TAG, "handleForwardingSettingsReadResult: " + idx); 944 Throwable error = null; 945 if (ar.exception != null) { 946 if (DBG) Log.d(LOG_TAG, "FwdRead: ar.exception=" + 947 ar.exception.getMessage()); 948 error = ar.exception; 949 } 950 if (ar.userObj instanceof Throwable) { 951 if (DBG) Log.d(LOG_TAG, "FwdRead: userObj=" + 952 ((Throwable)ar.userObj).getMessage()); 953 error = (Throwable)ar.userObj; 954 } 955 956 // We may have already gotten an error and decided to ignore the other results. 957 if (mForwardingReadResults == null) { 958 if (DBG) Log.d(LOG_TAG, "ignoring fwd reading result: " + idx); 959 return; 960 } 961 962 // In case of error ignore other results, show an error dialog 963 if (error != null) { 964 if (DBG) Log.d(LOG_TAG, "Error discovered for fwd read : " + idx); 965 mForwardingReadResults = null; 966 dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG); 967 showVMDialog(MSG_FW_GET_EXCEPTION); 968 return; 969 } 970 971 // Get the forwarding info 972 final CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result; 973 CallForwardInfo fi = null; 974 for (int i = 0 ; i < cfInfoArray.length; i++) { 975 if ((cfInfoArray[i].serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) { 976 fi = cfInfoArray[i]; 977 break; 978 } 979 } 980 if (fi == null) { 981 982 // In case we go nothing it means we need this reason disabled 983 // so create a CallForwardInfo for capturing this 984 if (DBG) Log.d(LOG_TAG, "Creating default info for " + idx); 985 fi = new CallForwardInfo(); 986 fi.status = 0; 987 fi.reason = FORWARDING_SETTINGS_REASONS[idx]; 988 fi.serviceClass = CommandsInterface.SERVICE_CLASS_VOICE; 989 } else { 990 // if there is not a forwarding number, ensure the entry is set to "not active." 991 if (fi.number == null || fi.number.length() == 0) { 992 fi.status = 0; 993 } 994 995 if (DBG) Log.d(LOG_TAG, "Got " + fi.toString() + " for " + idx); 996 } 997 mForwardingReadResults[idx] = fi; 998 999 // Check if we got all the results already 1000 boolean done = true; 1001 for (int i = 0; i < mForwardingReadResults.length; i++) { 1002 if (mForwardingReadResults[i] == null) { 1003 done = false; 1004 break; 1005 } 1006 } 1007 if (done) { 1008 if (DBG) Log.d(LOG_TAG, "Done receiving fwd info"); 1009 dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG); 1010 if (mReadingSettingsForDefaultProvider) { 1011 maybeSaveSettingsForVoicemailProvider(DEFAULT_VM_PROVIDER_KEY, 1012 new VoiceMailProviderSettings(this.mOldVmNumber, 1013 mForwardingReadResults)); 1014 mReadingSettingsForDefaultProvider = false; 1015 } 1016 saveVoiceMailAndForwardingNumberStage2(); 1017 } else { 1018 if (DBG) Log.d(LOG_TAG, "Not done receiving fwd info"); 1019 } 1020 } 1021 1022 private CallForwardInfo infoForReason(CallForwardInfo[] infos, int reason) { 1023 CallForwardInfo result = null; 1024 if (null != infos) { 1025 for (CallForwardInfo info : infos) { 1026 if (info.reason == reason) { 1027 result = info; 1028 break; 1029 } 1030 } 1031 } 1032 return result; 1033 } 1034 1035 private boolean isUpdateRequired(CallForwardInfo oldInfo, 1036 CallForwardInfo newInfo) { 1037 boolean result = true; 1038 if (0 == newInfo.status) { 1039 // If we're disabling a type of forwarding, and it's already 1040 // disabled for the account, don't make any change 1041 if (oldInfo != null && oldInfo.status == 0) { 1042 result = false; 1043 } 1044 } 1045 return result; 1046 } 1047 1048 private void resetForwardingChangeState() { 1049 mForwardingChangeResults = new HashMap<Integer, AsyncResult>(); 1050 mExpectedChangeResultReasons = new HashSet<Integer>(); 1051 } 1052 1053 // Called after we are done saving the previous forwarding settings if 1054 // we needed. 1055 private void saveVoiceMailAndForwardingNumberStage2() { 1056 mForwardingChangeResults = null; 1057 mVoicemailChangeResult = null; 1058 if (mNewFwdSettings != FWD_SETTINGS_DONT_TOUCH) { 1059 resetForwardingChangeState(); 1060 for (int i = 0; i < mNewFwdSettings.length; i++) { 1061 CallForwardInfo fi = mNewFwdSettings[i]; 1062 1063 final boolean doUpdate = isUpdateRequired(infoForReason( 1064 mForwardingReadResults, fi.reason), fi); 1065 1066 if (doUpdate) { 1067 if (DBG) log("Setting fwd #: " + i + ": " + fi.toString()); 1068 mExpectedChangeResultReasons.add(i); 1069 1070 mPhone.setCallForwardingOption( 1071 fi.status == 1 ? 1072 CommandsInterface.CF_ACTION_REGISTRATION : 1073 CommandsInterface.CF_ACTION_DISABLE, 1074 fi.reason, 1075 fi.number, 1076 fi.timeSeconds, 1077 mSetOptionComplete.obtainMessage( 1078 EVENT_FORWARDING_CHANGED, fi.reason, 0)); 1079 } 1080 } 1081 showDialogIfForeground(VOICEMAIL_FWD_SAVING_DIALOG); 1082 } else { 1083 if (DBG) log("Not touching fwd #"); 1084 setVMNumberWithCarrier(); 1085 } 1086 } 1087 1088 private void setVMNumberWithCarrier() { 1089 if (DBG) log("save voicemail #: " + mNewVMNumber); 1090 mPhone.setVoiceMailNumber( 1091 mPhone.getVoiceMailAlphaTag().toString(), 1092 mNewVMNumber, 1093 Message.obtain(mSetOptionComplete, EVENT_VOICEMAIL_CHANGED)); 1094 } 1095 1096 /** 1097 * Callback to handle option update completions 1098 */ 1099 private final Handler mSetOptionComplete = new Handler() { 1100 @Override 1101 public void handleMessage(Message msg) { 1102 AsyncResult result = (AsyncResult) msg.obj; 1103 boolean done = false; 1104 switch (msg.what) { 1105 case EVENT_VOICEMAIL_CHANGED: 1106 mVoicemailChangeResult = result; 1107 mVMChangeCompletedSuccessfully = checkVMChangeSuccess() == null; 1108 if (DBG) log("VM change complete msg, VM change done = " + 1109 String.valueOf(mVMChangeCompletedSuccessfully)); 1110 done = true; 1111 break; 1112 case EVENT_FORWARDING_CHANGED: 1113 mForwardingChangeResults.put(msg.arg1, result); 1114 if (result.exception != null) { 1115 Log.w(LOG_TAG, "Error in setting fwd# " + msg.arg1 + ": " + 1116 result.exception.getMessage()); 1117 } else { 1118 if (DBG) log("Success in setting fwd# " + msg.arg1); 1119 } 1120 final boolean completed = checkForwardingCompleted(); 1121 if (completed) { 1122 if (checkFwdChangeSuccess() == null) { 1123 if (DBG) log("Overall fwd changes completed ok, starting vm change"); 1124 setVMNumberWithCarrier(); 1125 } else { 1126 Log.w(LOG_TAG, "Overall fwd changes completed in failure. " + 1127 "Check if we need to try rollback for some settings."); 1128 mFwdChangesRequireRollback = false; 1129 Iterator<Map.Entry<Integer,AsyncResult>> it = 1130 mForwardingChangeResults.entrySet().iterator(); 1131 while (it.hasNext()) { 1132 Map.Entry<Integer,AsyncResult> entry = it.next(); 1133 if (entry.getValue().exception == null) { 1134 // If at least one succeeded we have to revert 1135 Log.i(LOG_TAG, "Rollback will be required"); 1136 mFwdChangesRequireRollback = true; 1137 break; 1138 } 1139 } 1140 if (!mFwdChangesRequireRollback) { 1141 Log.i(LOG_TAG, "No rollback needed."); 1142 } 1143 done = true; 1144 } 1145 } 1146 break; 1147 default: 1148 // TODO: should never reach this, may want to throw exception 1149 } 1150 if (done) { 1151 if (DBG) log("All VM provider related changes done"); 1152 if (mForwardingChangeResults != null) { 1153 dismissDialogSafely(VOICEMAIL_FWD_SAVING_DIALOG); 1154 } 1155 handleSetVMOrFwdMessage(); 1156 } 1157 } 1158 }; 1159 1160 /** 1161 * Callback to handle option revert completions 1162 */ 1163 private final Handler mRevertOptionComplete = new Handler() { 1164 @Override 1165 public void handleMessage(Message msg) { 1166 AsyncResult result = (AsyncResult) msg.obj; 1167 switch (msg.what) { 1168 case EVENT_VOICEMAIL_CHANGED: 1169 mVoicemailChangeResult = result; 1170 if (DBG) log("VM revert complete msg"); 1171 break; 1172 case EVENT_FORWARDING_CHANGED: 1173 mForwardingChangeResults.put(msg.arg1, result); 1174 if (result.exception != null) { 1175 if (DBG) log("Error in reverting fwd# " + msg.arg1 + ": " + 1176 result.exception.getMessage()); 1177 } else { 1178 if (DBG) log("Success in reverting fwd# " + msg.arg1); 1179 } 1180 if (DBG) log("FWD revert complete msg "); 1181 break; 1182 default: 1183 // TODO: should never reach this, may want to throw exception 1184 } 1185 final boolean done = 1186 (!mVMChangeCompletedSuccessfully || mVoicemailChangeResult != null) && 1187 (!mFwdChangesRequireRollback || checkForwardingCompleted()); 1188 if (done) { 1189 if (DBG) log("All VM reverts done"); 1190 dismissDialogSafely(VOICEMAIL_REVERTING_DIALOG); 1191 onRevertDone(); 1192 } 1193 } 1194 }; 1195 1196 /** 1197 * @return true if forwarding change has completed 1198 */ 1199 private boolean checkForwardingCompleted() { 1200 boolean result; 1201 if (mForwardingChangeResults == null) { 1202 result = true; 1203 } else { 1204 // return true iff there is a change result for every reason for 1205 // which we expected a result 1206 result = true; 1207 for (Integer reason : mExpectedChangeResultReasons) { 1208 if (mForwardingChangeResults.get(reason) == null) { 1209 result = false; 1210 break; 1211 } 1212 } 1213 } 1214 return result; 1215 } 1216 /** 1217 * @return error string or null if successful 1218 */ 1219 private String checkFwdChangeSuccess() { 1220 String result = null; 1221 Iterator<Map.Entry<Integer,AsyncResult>> it = 1222 mForwardingChangeResults.entrySet().iterator(); 1223 while (it.hasNext()) { 1224 Map.Entry<Integer,AsyncResult> entry = it.next(); 1225 Throwable exception = entry.getValue().exception; 1226 if (exception != null) { 1227 result = exception.getMessage(); 1228 if (result == null) { 1229 result = ""; 1230 } 1231 break; 1232 } 1233 } 1234 return result; 1235 } 1236 1237 /** 1238 * @return error string or null if successful 1239 */ 1240 private String checkVMChangeSuccess() { 1241 if (mVoicemailChangeResult.exception != null) { 1242 final String msg = mVoicemailChangeResult.exception.getMessage(); 1243 if (msg == null) { 1244 return ""; 1245 } 1246 return msg; 1247 } 1248 return null; 1249 } 1250 1251 private void handleSetVMOrFwdMessage() { 1252 if (DBG) { 1253 log("handleSetVMMessage: set VM request complete"); 1254 } 1255 boolean success = true; 1256 boolean fwdFailure = false; 1257 String exceptionMessage = ""; 1258 if (mForwardingChangeResults != null) { 1259 exceptionMessage = checkFwdChangeSuccess(); 1260 if (exceptionMessage != null) { 1261 success = false; 1262 fwdFailure = true; 1263 } 1264 } 1265 if (success) { 1266 exceptionMessage = checkVMChangeSuccess(); 1267 if (exceptionMessage != null) { 1268 success = false; 1269 } 1270 } 1271 if (success) { 1272 if (DBG) log("change VM success!"); 1273 handleVMAndFwdSetSuccess(MSG_VM_OK); 1274 } else { 1275 if (fwdFailure) { 1276 Log.w(LOG_TAG, "Failed to change fowarding setting. Reason: " + exceptionMessage); 1277 handleVMOrFwdSetError(MSG_FW_SET_EXCEPTION); 1278 } else { 1279 Log.w(LOG_TAG, "Failed to change voicemail. Reason: " + exceptionMessage); 1280 handleVMOrFwdSetError(MSG_VM_EXCEPTION); 1281 } 1282 } 1283 } 1284 1285 /** 1286 * Called when Voicemail Provider or its forwarding settings failed. Rolls back partly made 1287 * changes to those settings and show "failure" dialog. 1288 * 1289 * @param msgId Message ID used for the specific error case. {@link #MSG_FW_SET_EXCEPTION} or 1290 * {@link #MSG_VM_EXCEPTION} 1291 */ 1292 private void handleVMOrFwdSetError(int msgId) { 1293 if (mChangingVMorFwdDueToProviderChange) { 1294 mVMOrFwdSetError = msgId; 1295 mChangingVMorFwdDueToProviderChange = false; 1296 switchToPreviousVoicemailProvider(); 1297 return; 1298 } 1299 mChangingVMorFwdDueToProviderChange = false; 1300 showVMDialog(msgId); 1301 updateVoiceNumberField(); 1302 } 1303 1304 /** 1305 * Called when Voicemail Provider and its forwarding settings were successfully finished. 1306 * This updates a bunch of variables and show "success" dialog. 1307 */ 1308 private void handleVMAndFwdSetSuccess(int msg) { 1309 if (DBG) { 1310 log("handleVMAndFwdSetSuccess(). current voicemail provider key: " 1311 + getCurrentVoicemailProviderKey()); 1312 } 1313 mPreviousVMProviderKey = getCurrentVoicemailProviderKey(); 1314 mChangingVMorFwdDueToProviderChange = false; 1315 showVMDialog(msg); 1316 updateVoiceNumberField(); 1317 } 1318 1319 /** 1320 * Update the voicemail number from what we've recorded on the sim. 1321 */ 1322 private void updateVoiceNumberField() { 1323 if (DBG) { 1324 log("updateVoiceNumberField(). mSubMenuVoicemailSettings=" + mSubMenuVoicemailSettings); 1325 } 1326 if (mSubMenuVoicemailSettings == null) { 1327 return; 1328 } 1329 1330 mOldVmNumber = mPhone.getVoiceMailNumber(); 1331 if (mOldVmNumber == null) { 1332 mOldVmNumber = ""; 1333 } 1334 mSubMenuVoicemailSettings.setPhoneNumber(mOldVmNumber); 1335 final String summary = (mOldVmNumber.length() > 0) ? mOldVmNumber : 1336 getString(R.string.voicemail_number_not_set); 1337 mSubMenuVoicemailSettings.setSummary(summary); 1338 } 1339 1340 /* 1341 * Helper Methods for Activity class. 1342 * The initial query commands are split into two pieces now 1343 * for individual expansion. This combined with the ability 1344 * to cancel queries allows for a much better user experience, 1345 * and also ensures that the user only waits to update the 1346 * data that is relevant. 1347 */ 1348 1349 @Override 1350 protected void onPrepareDialog(int id, Dialog dialog) { 1351 super.onPrepareDialog(id, dialog); 1352 mCurrentDialogId = id; 1353 } 1354 1355 // dialog creation method, called by showDialog() 1356 @Override 1357 protected Dialog onCreateDialog(int id) { 1358 if ((id == VM_RESPONSE_ERROR) || (id == VM_NOCHANGE_ERROR) || 1359 (id == FW_SET_RESPONSE_ERROR) || (id == FW_GET_RESPONSE_ERROR) || 1360 (id == VOICEMAIL_DIALOG_CONFIRM)) { 1361 1362 AlertDialog.Builder b = new AlertDialog.Builder(this); 1363 1364 int msgId; 1365 int titleId = R.string.error_updating_title; 1366 switch (id) { 1367 case VOICEMAIL_DIALOG_CONFIRM: 1368 msgId = R.string.vm_changed; 1369 titleId = R.string.voicemail; 1370 // Set Button 2 1371 b.setNegativeButton(R.string.close_dialog, this); 1372 break; 1373 case VM_NOCHANGE_ERROR: 1374 // even though this is technically an error, 1375 // keep the title friendly. 1376 msgId = R.string.no_change; 1377 titleId = R.string.voicemail; 1378 // Set Button 2 1379 b.setNegativeButton(R.string.close_dialog, this); 1380 break; 1381 case VM_RESPONSE_ERROR: 1382 msgId = R.string.vm_change_failed; 1383 // Set Button 1 1384 b.setPositiveButton(R.string.close_dialog, this); 1385 break; 1386 case FW_SET_RESPONSE_ERROR: 1387 msgId = R.string.fw_change_failed; 1388 // Set Button 1 1389 b.setPositiveButton(R.string.close_dialog, this); 1390 break; 1391 case FW_GET_RESPONSE_ERROR: 1392 msgId = R.string.fw_get_in_vm_failed; 1393 b.setPositiveButton(R.string.alert_dialog_yes, this); 1394 b.setNegativeButton(R.string.alert_dialog_no, this); 1395 break; 1396 default: 1397 msgId = R.string.exception_error; 1398 // Set Button 3, tells the activity that the error is 1399 // not recoverable on dialog exit. 1400 b.setNeutralButton(R.string.close_dialog, this); 1401 break; 1402 } 1403 1404 b.setTitle(getText(titleId)); 1405 String message = getText(msgId).toString(); 1406 b.setMessage(message); 1407 b.setCancelable(false); 1408 AlertDialog dialog = b.create(); 1409 1410 // make the dialog more obvious by bluring the background. 1411 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 1412 1413 return dialog; 1414 } else if (id == VOICEMAIL_FWD_SAVING_DIALOG || id == VOICEMAIL_FWD_READING_DIALOG || 1415 id == VOICEMAIL_REVERTING_DIALOG) { 1416 ProgressDialog dialog = new ProgressDialog(this); 1417 dialog.setTitle(getText(R.string.updating_title)); 1418 dialog.setIndeterminate(true); 1419 dialog.setCancelable(false); 1420 dialog.setMessage(getText( 1421 id == VOICEMAIL_FWD_SAVING_DIALOG ? R.string.updating_settings : 1422 (id == VOICEMAIL_REVERTING_DIALOG ? R.string.reverting_settings : 1423 R.string.reading_settings))); 1424 return dialog; 1425 } 1426 1427 1428 return null; 1429 } 1430 1431 // This is a method implemented for DialogInterface.OnClickListener. 1432 // Used with the error dialog to close the app, voicemail dialog to just dismiss. 1433 // Close button is mapped to BUTTON_POSITIVE for the errors that close the activity, 1434 // while those that are mapped to BUTTON_NEUTRAL only move the preference focus. 1435 public void onClick(DialogInterface dialog, int which) { 1436 dialog.dismiss(); 1437 switch (which){ 1438 case DialogInterface.BUTTON_NEUTRAL: 1439 if (DBG) log("Neutral button"); 1440 break; 1441 case DialogInterface.BUTTON_NEGATIVE: 1442 if (DBG) log("Negative button"); 1443 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) { 1444 // We failed to get current forwarding settings and the user 1445 // does not wish to continue. 1446 switchToPreviousVoicemailProvider(); 1447 } 1448 break; 1449 case DialogInterface.BUTTON_POSITIVE: 1450 if (DBG) log("Positive button"); 1451 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) { 1452 // We failed to get current forwarding settings but the user 1453 // wishes to continue changing settings to the new vm provider 1454 saveVoiceMailAndForwardingNumberStage2(); 1455 } else { 1456 finish(); 1457 } 1458 return; 1459 default: 1460 // just let the dialog close and go back to the input 1461 } 1462 // In all dialogs, all buttons except BUTTON_POSITIVE lead to the end of user interaction 1463 // with settings UI. If we were called to explicitly configure voice mail then 1464 // we finish the settings activity here to come back to whatever the user was doing. 1465 if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) { 1466 finish(); 1467 } 1468 } 1469 1470 // set the app state with optional status. 1471 private void showVMDialog(int msgStatus) { 1472 switch (msgStatus) { 1473 // It's a bit worrisome to punt in the error cases here when we're 1474 // not in the foreground; maybe toast instead? 1475 case MSG_VM_EXCEPTION: 1476 showDialogIfForeground(VM_RESPONSE_ERROR); 1477 break; 1478 case MSG_FW_SET_EXCEPTION: 1479 showDialogIfForeground(FW_SET_RESPONSE_ERROR); 1480 break; 1481 case MSG_FW_GET_EXCEPTION: 1482 showDialogIfForeground(FW_GET_RESPONSE_ERROR); 1483 break; 1484 case MSG_VM_NOCHANGE: 1485 showDialogIfForeground(VM_NOCHANGE_ERROR); 1486 break; 1487 case MSG_VM_OK: 1488 showDialogIfForeground(VOICEMAIL_DIALOG_CONFIRM); 1489 break; 1490 case MSG_OK: 1491 default: 1492 // This should never happen. 1493 } 1494 } 1495 1496 /* 1497 * Activity class methods 1498 */ 1499 1500 @Override 1501 protected void onCreate(Bundle icicle) { 1502 super.onCreate(icicle); 1503 if (DBG) log("onCreate(). Intent: " + getIntent()); 1504 mPhone = PhoneGlobals.getPhone(); 1505 1506 addPreferencesFromResource(R.xml.call_feature_setting); 1507 1508 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 1509 1510 // get buttons 1511 PreferenceScreen prefSet = getPreferenceScreen(); 1512 mSubMenuVoicemailSettings = (EditPhoneNumberPreference)findPreference(BUTTON_VOICEMAIL_KEY); 1513 if (mSubMenuVoicemailSettings != null) { 1514 mSubMenuVoicemailSettings.setParentActivity(this, VOICEMAIL_PREF_ID, this); 1515 mSubMenuVoicemailSettings.setDialogOnClosedListener(this); 1516 mSubMenuVoicemailSettings.setDialogTitle(R.string.voicemail_settings_number_label); 1517 } 1518 1519 mRingtonePreference = findPreference(BUTTON_RINGTONE_KEY); 1520 mVibrateWhenRinging = (CheckBoxPreference) findPreference(BUTTON_VIBRATE_ON_RING); 1521 mPlayDtmfTone = (CheckBoxPreference) findPreference(BUTTON_PLAY_DTMF_TONE); 1522 mButtonDTMF = (ListPreference) findPreference(BUTTON_DTMF_KEY); 1523 mButtonAutoRetry = (CheckBoxPreference) findPreference(BUTTON_RETRY_KEY); 1524 mButtonHAC = (CheckBoxPreference) findPreference(BUTTON_HAC_KEY); 1525 mButtonTTY = (ListPreference) findPreference(BUTTON_TTY_KEY); 1526 mVoicemailProviders = (ListPreference) findPreference(BUTTON_VOICEMAIL_PROVIDER_KEY); 1527 if (mVoicemailProviders != null) { 1528 mVoicemailProviders.setOnPreferenceChangeListener(this); 1529 mVoicemailSettings = (PreferenceScreen)findPreference(BUTTON_VOICEMAIL_SETTING_KEY); 1530 mVoicemailNotificationRingtone = 1531 findPreference(BUTTON_VOICEMAIL_NOTIFICATION_RINGTONE_KEY); 1532 mVoicemailNotificationVibrate = 1533 (CheckBoxPreference) findPreference(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY); 1534 initVoiceMailProviders(); 1535 } 1536 1537 if (mVibrateWhenRinging != null) { 1538 Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); 1539 if (vibrator != null && vibrator.hasVibrator()) { 1540 mVibrateWhenRinging.setOnPreferenceChangeListener(this); 1541 } else { 1542 prefSet.removePreference(mVibrateWhenRinging); 1543 mVibrateWhenRinging = null; 1544 } 1545 } 1546 1547 final ContentResolver contentResolver = getContentResolver(); 1548 1549 if (mPlayDtmfTone != null) { 1550 mPlayDtmfTone.setChecked(Settings.System.getInt(contentResolver, 1551 Settings.System.DTMF_TONE_WHEN_DIALING, 1) != 0); 1552 } 1553 1554 if (mButtonDTMF != null) { 1555 if (getResources().getBoolean(R.bool.dtmf_type_enabled)) { 1556 mButtonDTMF.setOnPreferenceChangeListener(this); 1557 } else { 1558 prefSet.removePreference(mButtonDTMF); 1559 mButtonDTMF = null; 1560 } 1561 } 1562 1563 if (mButtonAutoRetry != null) { 1564 if (getResources().getBoolean(R.bool.auto_retry_enabled)) { 1565 mButtonAutoRetry.setOnPreferenceChangeListener(this); 1566 } else { 1567 prefSet.removePreference(mButtonAutoRetry); 1568 mButtonAutoRetry = null; 1569 } 1570 } 1571 1572 if (mButtonHAC != null) { 1573 if (getResources().getBoolean(R.bool.hac_enabled)) { 1574 1575 mButtonHAC.setOnPreferenceChangeListener(this); 1576 } else { 1577 prefSet.removePreference(mButtonHAC); 1578 mButtonHAC = null; 1579 } 1580 } 1581 1582 if (mButtonTTY != null) { 1583 if (getResources().getBoolean(R.bool.tty_enabled)) { 1584 mButtonTTY.setOnPreferenceChangeListener(this); 1585 } else { 1586 prefSet.removePreference(mButtonTTY); 1587 mButtonTTY = null; 1588 } 1589 } 1590 1591 if (!getResources().getBoolean(R.bool.world_phone)) { 1592 Preference options = prefSet.findPreference(BUTTON_CDMA_OPTIONS); 1593 if (options != null) 1594 prefSet.removePreference(options); 1595 options = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS); 1596 if (options != null) 1597 prefSet.removePreference(options); 1598 1599 int phoneType = mPhone.getPhoneType(); 1600 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 1601 Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY); 1602 if (fdnButton != null) 1603 prefSet.removePreference(fdnButton); 1604 if (!getResources().getBoolean(R.bool.config_voice_privacy_disable)) { 1605 addPreferencesFromResource(R.xml.cdma_call_privacy); 1606 } 1607 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { 1608 addPreferencesFromResource(R.xml.gsm_umts_call_options); 1609 } else { 1610 throw new IllegalStateException("Unexpected phone type: " + phoneType); 1611 } 1612 } 1613 1614 // create intent to bring up contact list 1615 mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT); 1616 mContactListIntent.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE); 1617 1618 // check the intent that started this activity and pop up the voicemail 1619 // dialog if we've been asked to. 1620 // If we have at least one non default VM provider registered then bring up 1621 // the selection for the VM provider, otherwise bring up a VM number dialog. 1622 // We only bring up the dialog the first time we are called (not after orientation change) 1623 if (icicle == null) { 1624 if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL) && 1625 mVoicemailProviders != null) { 1626 if (DBG) { 1627 log("ACTION_ADD_VOICEMAIL Intent is thrown. current VM data size: " 1628 + mVMProvidersData.size()); 1629 } 1630 if (mVMProvidersData.size() > 1) { 1631 simulatePreferenceClick(mVoicemailProviders); 1632 } else { 1633 onPreferenceChange(mVoicemailProviders, DEFAULT_VM_PROVIDER_KEY); 1634 mVoicemailProviders.setValue(DEFAULT_VM_PROVIDER_KEY); 1635 } 1636 } 1637 } 1638 updateVoiceNumberField(); 1639 mVMProviderSettingsForced = false; 1640 createSipCallSettings(); 1641 1642 mRingtoneLookupRunnable = new Runnable() { 1643 @Override 1644 public void run() { 1645 if (mRingtonePreference != null) { 1646 updateRingtoneName(RingtoneManager.TYPE_RINGTONE, mRingtonePreference, 1647 MSG_UPDATE_RINGTONE_SUMMARY); 1648 } 1649 if (mVoicemailNotificationRingtone != null) { 1650 updateRingtoneName(RingtoneManager.TYPE_NOTIFICATION, 1651 mVoicemailNotificationRingtone, MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY); 1652 } 1653 } 1654 }; 1655 1656 ActionBar actionBar = getActionBar(); 1657 if (actionBar != null) { 1658 // android.R.id.home will be triggered in onOptionsItemSelected() 1659 actionBar.setDisplayShowHomeEnabled(true); 1660 actionBar.setDisplayHomeAsUpEnabled(true); 1661 actionBar.setDisplayShowTitleEnabled(true); 1662 } 1663 } 1664 1665 /** 1666 * Updates ringtone name. This is a method copied from com.android.settings.SoundSettings 1667 * 1668 * @see com.android.settings.SoundSettings 1669 */ 1670 private void updateRingtoneName(int type, Preference preference, int msg) { 1671 if (preference == null) return; 1672 final Uri ringtoneUri; 1673 boolean defaultRingtone = false; 1674 if (type == RingtoneManager.TYPE_RINGTONE) { 1675 // For ringtones, we can just lookup the system default because changing the settings 1676 // in Call Settings changes the system default. 1677 ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, type); 1678 } else { 1679 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( 1680 mPhone.getContext()); 1681 // for voicemail notifications, we use the value saved in Phone's shared preferences. 1682 String uriString = prefs.getString(preference.getKey(), null); 1683 if (TextUtils.isEmpty(uriString)) { 1684 // silent ringtone 1685 ringtoneUri = null; 1686 } else { 1687 if (uriString.equals(Settings.System.DEFAULT_NOTIFICATION_URI.toString())) { 1688 // If it turns out that the voicemail notification is set to the system 1689 // default notification, we retrieve the actual URI to prevent it from showing 1690 // up as "Unknown Ringtone". 1691 defaultRingtone = true; 1692 ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, type); 1693 } else { 1694 ringtoneUri = Uri.parse(uriString); 1695 } 1696 } 1697 } 1698 CharSequence summary = getString(com.android.internal.R.string.ringtone_unknown); 1699 // Is it a silent ringtone? 1700 if (ringtoneUri == null) { 1701 summary = getString(com.android.internal.R.string.ringtone_silent); 1702 } else { 1703 // Fetch the ringtone title from the media provider 1704 try { 1705 Cursor cursor = getContentResolver().query(ringtoneUri, 1706 new String[] { MediaStore.Audio.Media.TITLE }, null, null, null); 1707 if (cursor != null) { 1708 if (cursor.moveToFirst()) { 1709 summary = cursor.getString(0); 1710 } 1711 cursor.close(); 1712 } 1713 } catch (SQLiteException sqle) { 1714 // Unknown title for the ringtone 1715 } 1716 } 1717 if (defaultRingtone) { 1718 summary = mPhone.getContext().getString( 1719 R.string.default_notification_description, summary); 1720 } 1721 mRingtoneLookupComplete.sendMessage(mRingtoneLookupComplete.obtainMessage(msg, summary)); 1722 } 1723 1724 private void createSipCallSettings() { 1725 // Add Internet call settings. 1726 if (PhoneUtils.isVoipSupported()) { 1727 mSipManager = SipManager.newInstance(this); 1728 mSipSharedPreferences = new SipSharedPreferences(this); 1729 addPreferencesFromResource(R.xml.sip_settings_category); 1730 mButtonSipCallOptions = getSipCallOptionPreference(); 1731 mButtonSipCallOptions.setOnPreferenceChangeListener(this); 1732 mButtonSipCallOptions.setValueIndex( 1733 mButtonSipCallOptions.findIndexOfValue( 1734 mSipSharedPreferences.getSipCallOption())); 1735 mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry()); 1736 } 1737 } 1738 1739 // Gets the call options for SIP depending on whether SIP is allowed only 1740 // on Wi-Fi only; also make the other options preference invisible. 1741 private ListPreference getSipCallOptionPreference() { 1742 ListPreference wifiAnd3G = (ListPreference) 1743 findPreference(BUTTON_SIP_CALL_OPTIONS); 1744 ListPreference wifiOnly = (ListPreference) 1745 findPreference(BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY); 1746 PreferenceGroup sipSettings = (PreferenceGroup) 1747 findPreference(SIP_SETTINGS_CATEGORY_KEY); 1748 if (SipManager.isSipWifiOnly(this)) { 1749 sipSettings.removePreference(wifiAnd3G); 1750 return wifiOnly; 1751 } else { 1752 sipSettings.removePreference(wifiOnly); 1753 return wifiAnd3G; 1754 } 1755 } 1756 1757 @Override 1758 protected void onResume() { 1759 super.onResume(); 1760 mForeground = true; 1761 1762 if (isAirplaneModeOn()) { 1763 Preference sipSettings = findPreference(SIP_SETTINGS_CATEGORY_KEY); 1764 PreferenceScreen screen = getPreferenceScreen(); 1765 int count = screen.getPreferenceCount(); 1766 for (int i = 0 ; i < count ; ++i) { 1767 Preference pref = screen.getPreference(i); 1768 if (pref != sipSettings) pref.setEnabled(false); 1769 } 1770 return; 1771 } 1772 1773 if (mVibrateWhenRinging != null) { 1774 mVibrateWhenRinging.setChecked(getVibrateWhenRinging(this)); 1775 } 1776 1777 if (mButtonDTMF != null) { 1778 int dtmf = Settings.System.getInt(getContentResolver(), 1779 Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, Constants.DTMF_TONE_TYPE_NORMAL); 1780 mButtonDTMF.setValueIndex(dtmf); 1781 } 1782 1783 if (mButtonAutoRetry != null) { 1784 int autoretry = Settings.Global.getInt(getContentResolver(), 1785 Settings.Global.CALL_AUTO_RETRY, 0); 1786 mButtonAutoRetry.setChecked(autoretry != 0); 1787 } 1788 1789 if (mButtonHAC != null) { 1790 int hac = Settings.System.getInt(getContentResolver(), Settings.System.HEARING_AID, 0); 1791 mButtonHAC.setChecked(hac != 0); 1792 } 1793 1794 if (mButtonTTY != null) { 1795 int settingsTtyMode = Settings.Secure.getInt(getContentResolver(), 1796 Settings.Secure.PREFERRED_TTY_MODE, 1797 Phone.TTY_MODE_OFF); 1798 mButtonTTY.setValue(Integer.toString(settingsTtyMode)); 1799 updatePreferredTtyModeSummary(settingsTtyMode); 1800 } 1801 1802 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( 1803 mPhone.getContext()); 1804 if (migrateVoicemailVibrationSettingsIfNeeded(prefs)) { 1805 mVoicemailNotificationVibrate.setChecked(prefs.getBoolean( 1806 BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, false)); 1807 } 1808 1809 lookupRingtoneName(); 1810 } 1811 1812 // Migrate settings from BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY to 1813 // BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, if the latter does not exist. 1814 // Returns true if migration was performed. 1815 public static boolean migrateVoicemailVibrationSettingsIfNeeded(SharedPreferences prefs) { 1816 if (!prefs.contains(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY)) { 1817 String vibrateWhen = prefs.getString( 1818 BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY, VOICEMAIL_VIBRATION_NEVER); 1819 // If vibrateWhen is always, then voicemailVibrate should be True. 1820 // otherwise if vibrateWhen is "only in silent mode", or "never", then 1821 // voicemailVibrate = False. 1822 boolean voicemailVibrate = vibrateWhen.equals(VOICEMAIL_VIBRATION_ALWAYS); 1823 final SharedPreferences.Editor editor = prefs.edit(); 1824 editor.putBoolean(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, voicemailVibrate); 1825 editor.commit(); 1826 return true; 1827 } 1828 return false; 1829 } 1830 1831 /** 1832 * Obtain the setting for "vibrate when ringing" setting. 1833 * 1834 * Watch out: if the setting is missing in the device, this will try obtaining the old 1835 * "vibrate on ring" setting from AudioManager, and save the previous setting to the new one. 1836 */ 1837 public static boolean getVibrateWhenRinging(Context context) { 1838 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 1839 if (vibrator == null || !vibrator.hasVibrator()) { 1840 return false; 1841 } 1842 return Settings.System.getInt(context.getContentResolver(), 1843 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0; 1844 } 1845 1846 /** 1847 * Lookups ringtone name asynchronously and updates the relevant Preference. 1848 */ 1849 private void lookupRingtoneName() { 1850 new Thread(mRingtoneLookupRunnable).start(); 1851 } 1852 1853 private boolean isAirplaneModeOn() { 1854 return Settings.System.getInt(getContentResolver(), 1855 Settings.System.AIRPLANE_MODE_ON, 0) != 0; 1856 } 1857 1858 private void handleTTYChange(Preference preference, Object objValue) { 1859 int buttonTtyMode; 1860 buttonTtyMode = Integer.valueOf((String) objValue).intValue(); 1861 int settingsTtyMode = android.provider.Settings.Secure.getInt( 1862 getContentResolver(), 1863 android.provider.Settings.Secure.PREFERRED_TTY_MODE, preferredTtyMode); 1864 if (DBG) log("handleTTYChange: requesting set TTY mode enable (TTY) to" + 1865 Integer.toString(buttonTtyMode)); 1866 1867 if (buttonTtyMode != settingsTtyMode) { 1868 switch(buttonTtyMode) { 1869 case Phone.TTY_MODE_OFF: 1870 case Phone.TTY_MODE_FULL: 1871 case Phone.TTY_MODE_HCO: 1872 case Phone.TTY_MODE_VCO: 1873 android.provider.Settings.Secure.putInt(getContentResolver(), 1874 android.provider.Settings.Secure.PREFERRED_TTY_MODE, buttonTtyMode); 1875 break; 1876 default: 1877 buttonTtyMode = Phone.TTY_MODE_OFF; 1878 } 1879 1880 mButtonTTY.setValue(Integer.toString(buttonTtyMode)); 1881 updatePreferredTtyModeSummary(buttonTtyMode); 1882 Intent ttyModeChanged = new Intent(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION); 1883 ttyModeChanged.putExtra(TtyIntent.TTY_PREFFERED_MODE, buttonTtyMode); 1884 sendBroadcastAsUser(ttyModeChanged, UserHandle.ALL); 1885 } 1886 } 1887 1888 private void handleSipCallOptionsChange(Object objValue) { 1889 String option = objValue.toString(); 1890 mSipSharedPreferences.setSipCallOption(option); 1891 mButtonSipCallOptions.setValueIndex( 1892 mButtonSipCallOptions.findIndexOfValue(option)); 1893 mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry()); 1894 } 1895 1896 private void updatePreferredTtyModeSummary(int TtyMode) { 1897 String [] txts = getResources().getStringArray(R.array.tty_mode_entries); 1898 switch(TtyMode) { 1899 case Phone.TTY_MODE_OFF: 1900 case Phone.TTY_MODE_HCO: 1901 case Phone.TTY_MODE_VCO: 1902 case Phone.TTY_MODE_FULL: 1903 mButtonTTY.setSummary(txts[TtyMode]); 1904 break; 1905 default: 1906 mButtonTTY.setEnabled(false); 1907 mButtonTTY.setSummary(txts[Phone.TTY_MODE_OFF]); 1908 } 1909 } 1910 1911 private static void log(String msg) { 1912 Log.d(LOG_TAG, msg); 1913 } 1914 1915 /** 1916 * Updates the look of the VM preference widgets based on current VM provider settings. 1917 * Note that the provider name is loaded form the found activity via loadLabel in 1918 * {@link #initVoiceMailProviders()} in order for it to be localizable. 1919 */ 1920 private void updateVMPreferenceWidgets(String currentProviderSetting) { 1921 final String key = currentProviderSetting; 1922 final VoiceMailProvider provider = mVMProvidersData.get(key); 1923 1924 /* This is the case when we are coming up on a freshly wiped phone and there is no 1925 persisted value for the list preference mVoicemailProviders. 1926 In this case we want to show the UI asking the user to select a voicemail provider as 1927 opposed to silently falling back to default one. */ 1928 if (provider == null) { 1929 if (DBG) { 1930 log("updateVMPreferenceWidget: provider for the key \"" + key + "\" is null."); 1931 } 1932 mVoicemailProviders.setSummary(getString(R.string.sum_voicemail_choose_provider)); 1933 mVoicemailSettings.setEnabled(false); 1934 mVoicemailSettings.setIntent(null); 1935 1936 mVoicemailNotificationVibrate.setEnabled(false); 1937 } else { 1938 if (DBG) { 1939 log("updateVMPreferenceWidget: provider for the key \"" + key + "\".." 1940 + "name: " + provider.name 1941 + ", intent: " + provider.intent); 1942 } 1943 final String providerName = provider.name; 1944 mVoicemailProviders.setSummary(providerName); 1945 mVoicemailSettings.setEnabled(true); 1946 mVoicemailSettings.setIntent(provider.intent); 1947 1948 mVoicemailNotificationVibrate.setEnabled(true); 1949 } 1950 } 1951 1952 /** 1953 * Enumerates existing VM providers and puts their data into the list and populates 1954 * the preference list objects with their names. 1955 * In case we are called with ACTION_ADD_VOICEMAIL intent the intent may have 1956 * an extra string called IGNORE_PROVIDER_EXTRA with "package.activityName" of the provider 1957 * which should be hidden when we bring up the list of possible VM providers to choose. 1958 */ 1959 private void initVoiceMailProviders() { 1960 if (DBG) log("initVoiceMailProviders()"); 1961 mPerProviderSavedVMNumbers = 1962 this.getApplicationContext().getSharedPreferences( 1963 VM_NUMBERS_SHARED_PREFERENCES_NAME, MODE_PRIVATE); 1964 1965 String providerToIgnore = null; 1966 if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) { 1967 if (getIntent().hasExtra(IGNORE_PROVIDER_EXTRA)) { 1968 providerToIgnore = getIntent().getStringExtra(IGNORE_PROVIDER_EXTRA); 1969 } 1970 if (DBG) log("Found ACTION_ADD_VOICEMAIL. providerToIgnore=" + providerToIgnore); 1971 if (providerToIgnore != null) { 1972 // IGNORE_PROVIDER_EXTRA implies we want to remove the choice from the list. 1973 deleteSettingsForVoicemailProvider(providerToIgnore); 1974 } 1975 } 1976 1977 mVMProvidersData.clear(); 1978 1979 // Stick the default element which is always there 1980 final String myCarrier = getString(R.string.voicemail_default); 1981 mVMProvidersData.put(DEFAULT_VM_PROVIDER_KEY, new VoiceMailProvider(myCarrier, null)); 1982 1983 // Enumerate providers 1984 PackageManager pm = getPackageManager(); 1985 Intent intent = new Intent(); 1986 intent.setAction(ACTION_CONFIGURE_VOICEMAIL); 1987 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0); 1988 int len = resolveInfos.size() + 1; // +1 for the default choice we will insert. 1989 1990 // Go through the list of discovered providers populating the data map 1991 // skip the provider we were instructed to ignore if there was one 1992 for (int i = 0; i < resolveInfos.size(); i++) { 1993 final ResolveInfo ri= resolveInfos.get(i); 1994 final ActivityInfo currentActivityInfo = ri.activityInfo; 1995 final String key = makeKeyForActivity(currentActivityInfo); 1996 if (key.equals(providerToIgnore)) { 1997 if (DBG) log("Ignoring key: " + key); 1998 len--; 1999 continue; 2000 } 2001 if (DBG) log("Loading key: " + key); 2002 final String nameForDisplay = ri.loadLabel(pm).toString(); 2003 Intent providerIntent = new Intent(); 2004 providerIntent.setAction(ACTION_CONFIGURE_VOICEMAIL); 2005 providerIntent.setClassName(currentActivityInfo.packageName, 2006 currentActivityInfo.name); 2007 if (DBG) { 2008 log("Store loaded VoiceMailProvider. key: " + key 2009 + " -> name: " + nameForDisplay + ", intent: " + providerIntent); 2010 } 2011 mVMProvidersData.put( 2012 key, 2013 new VoiceMailProvider(nameForDisplay, providerIntent)); 2014 2015 } 2016 2017 // Now we know which providers to display - create entries and values array for 2018 // the list preference 2019 String [] entries = new String [len]; 2020 String [] values = new String [len]; 2021 entries[0] = myCarrier; 2022 values[0] = DEFAULT_VM_PROVIDER_KEY; 2023 int entryIdx = 1; 2024 for (int i = 0; i < resolveInfos.size(); i++) { 2025 final String key = makeKeyForActivity(resolveInfos.get(i).activityInfo); 2026 if (!mVMProvidersData.containsKey(key)) { 2027 continue; 2028 } 2029 entries[entryIdx] = mVMProvidersData.get(key).name; 2030 values[entryIdx] = key; 2031 entryIdx++; 2032 } 2033 2034 // ListPreference is now updated. 2035 mVoicemailProviders.setEntries(entries); 2036 mVoicemailProviders.setEntryValues(values); 2037 2038 // Remember the current Voicemail Provider key as a "previous" key. This will be used 2039 // when we fail to update Voicemail Provider, which requires rollback. 2040 // We will update this when the VM Provider setting is successfully updated. 2041 mPreviousVMProviderKey = getCurrentVoicemailProviderKey(); 2042 if (DBG) log("Set up the first mPreviousVMProviderKey: " + mPreviousVMProviderKey); 2043 2044 // Finally update the preference texts. 2045 updateVMPreferenceWidgets(mPreviousVMProviderKey); 2046 } 2047 2048 private String makeKeyForActivity(ActivityInfo ai) { 2049 return ai.name; 2050 } 2051 2052 /** 2053 * Simulates user clicking on a passed preference. 2054 * Usually needed when the preference is a dialog preference and we want to invoke 2055 * a dialog for this preference programmatically. 2056 * TODO(iliat): figure out if there is a cleaner way to cause preference dlg to come up 2057 */ 2058 private void simulatePreferenceClick(Preference preference) { 2059 // Go through settings until we find our setting 2060 // and then simulate a click on it to bring up the dialog 2061 final ListAdapter adapter = getPreferenceScreen().getRootAdapter(); 2062 for (int idx = 0; idx < adapter.getCount(); idx++) { 2063 if (adapter.getItem(idx) == preference) { 2064 getPreferenceScreen().onItemClick(this.getListView(), 2065 null, idx, adapter.getItemId(idx)); 2066 break; 2067 } 2068 } 2069 } 2070 2071 /** 2072 * Saves new VM provider settings associating them with the currently selected 2073 * provider if settings are different than the ones already stored for this 2074 * provider. 2075 * Later on these will be used when the user switches a provider. 2076 */ 2077 private void maybeSaveSettingsForVoicemailProvider(String key, 2078 VoiceMailProviderSettings newSettings) { 2079 if (mVoicemailProviders == null) { 2080 return; 2081 } 2082 final VoiceMailProviderSettings curSettings = loadSettingsForVoiceMailProvider(key); 2083 if (newSettings.equals(curSettings)) { 2084 if (DBG) { 2085 log("maybeSaveSettingsForVoicemailProvider:" 2086 + " Not saving setting for " + key + " since they have not changed"); 2087 } 2088 return; 2089 } 2090 if (DBG) log("Saving settings for " + key + ": " + newSettings.toString()); 2091 Editor editor = mPerProviderSavedVMNumbers.edit(); 2092 editor.putString(key + VM_NUMBER_TAG, newSettings.voicemailNumber); 2093 String fwdKey = key + FWD_SETTINGS_TAG; 2094 CallForwardInfo[] s = newSettings.forwardingSettings; 2095 if (s != FWD_SETTINGS_DONT_TOUCH) { 2096 editor.putInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, s.length); 2097 for (int i = 0; i < s.length; i++) { 2098 final String settingKey = fwdKey + FWD_SETTING_TAG + String.valueOf(i); 2099 final CallForwardInfo fi = s[i]; 2100 editor.putInt(settingKey + FWD_SETTING_STATUS, fi.status); 2101 editor.putInt(settingKey + FWD_SETTING_REASON, fi.reason); 2102 editor.putString(settingKey + FWD_SETTING_NUMBER, fi.number); 2103 editor.putInt(settingKey + FWD_SETTING_TIME, fi.timeSeconds); 2104 } 2105 } else { 2106 editor.putInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, 0); 2107 } 2108 editor.apply(); 2109 } 2110 2111 /** 2112 * Returns settings previously stored for the currently selected 2113 * voice mail provider. If none is stored returns null. 2114 * If the user switches to a voice mail provider and we have settings 2115 * stored for it we will automatically change the phone's voice mail number 2116 * and forwarding number to the stored one. Otherwise we will bring up provider's configuration 2117 * UI. 2118 */ 2119 private VoiceMailProviderSettings loadSettingsForVoiceMailProvider(String key) { 2120 final String vmNumberSetting = mPerProviderSavedVMNumbers.getString(key + VM_NUMBER_TAG, 2121 null); 2122 if (vmNumberSetting == null) { 2123 Log.w(LOG_TAG, "VoiceMailProvider settings for the key \"" + key + "\"" 2124 + " was not found. Returning null."); 2125 return null; 2126 } 2127 2128 CallForwardInfo[] cfi = FWD_SETTINGS_DONT_TOUCH; 2129 String fwdKey = key + FWD_SETTINGS_TAG; 2130 final int fwdLen = mPerProviderSavedVMNumbers.getInt(fwdKey + FWD_SETTINGS_LENGTH_TAG, 0); 2131 if (fwdLen > 0) { 2132 cfi = new CallForwardInfo[fwdLen]; 2133 for (int i = 0; i < cfi.length; i++) { 2134 final String settingKey = fwdKey + FWD_SETTING_TAG + String.valueOf(i); 2135 cfi[i] = new CallForwardInfo(); 2136 cfi[i].status = mPerProviderSavedVMNumbers.getInt( 2137 settingKey + FWD_SETTING_STATUS, 0); 2138 cfi[i].reason = mPerProviderSavedVMNumbers.getInt( 2139 settingKey + FWD_SETTING_REASON, 2140 CommandsInterface.CF_REASON_ALL_CONDITIONAL); 2141 cfi[i].serviceClass = CommandsInterface.SERVICE_CLASS_VOICE; 2142 cfi[i].toa = PhoneNumberUtils.TOA_International; 2143 cfi[i].number = mPerProviderSavedVMNumbers.getString( 2144 settingKey + FWD_SETTING_NUMBER, ""); 2145 cfi[i].timeSeconds = mPerProviderSavedVMNumbers.getInt( 2146 settingKey + FWD_SETTING_TIME, 20); 2147 } 2148 } 2149 2150 VoiceMailProviderSettings settings = new VoiceMailProviderSettings(vmNumberSetting, cfi); 2151 if (DBG) log("Loaded settings for " + key + ": " + settings.toString()); 2152 return settings; 2153 } 2154 2155 /** 2156 * Deletes settings for the specified provider. 2157 */ 2158 private void deleteSettingsForVoicemailProvider(String key) { 2159 if (DBG) log("Deleting settings for" + key); 2160 if (mVoicemailProviders == null) { 2161 return; 2162 } 2163 mPerProviderSavedVMNumbers.edit() 2164 .putString(key + VM_NUMBER_TAG, null) 2165 .putInt(key + FWD_SETTINGS_TAG + FWD_SETTINGS_LENGTH_TAG, 0) 2166 .commit(); 2167 } 2168 2169 private String getCurrentVoicemailProviderKey() { 2170 final String key = mVoicemailProviders.getValue(); 2171 return (key != null) ? key : DEFAULT_VM_PROVIDER_KEY; 2172 } 2173 2174 @Override 2175 public boolean onOptionsItemSelected(MenuItem item) { 2176 final int itemId = item.getItemId(); 2177 if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() 2178 onBackPressed(); 2179 return true; 2180 } 2181 return super.onOptionsItemSelected(item); 2182 } 2183 2184 /** 2185 * Finish current Activity and go up to the top level Settings ({@link CallFeaturesSetting}). 2186 * This is useful for implementing "HomeAsUp" capability for second-level Settings. 2187 */ 2188 public static void goUpToTopLevelSetting(Activity activity) { 2189 Intent intent = new Intent(activity, CallFeaturesSetting.class); 2190 intent.setAction(Intent.ACTION_MAIN); 2191 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 2192 activity.startActivity(intent); 2193 activity.finish(); 2194 } 2195 } 2196