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