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