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