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