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