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