• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.phone.settings;
2 
3 import android.content.Context;
4 import android.content.Intent;
5 import android.content.pm.PackageManager;
6 import android.content.pm.ResolveInfo;
7 import android.graphics.drawable.Icon;
8 import android.net.sip.SipManager;
9 import android.os.Bundle;
10 import android.preference.CheckBoxPreference;
11 import android.preference.ListPreference;
12 import android.preference.Preference;
13 import android.preference.PreferenceCategory;
14 import android.preference.PreferenceFragment;
15 import android.telecom.PhoneAccount;
16 import android.telecom.PhoneAccountHandle;
17 import android.telecom.TelecomManager;
18 import android.telephony.SubscriptionInfo;
19 import android.telephony.SubscriptionManager;
20 import android.telephony.TelephonyManager;
21 import android.text.TextUtils;
22 import android.util.Log;
23 
24 import com.android.internal.telephony.Phone;
25 import com.android.phone.PhoneUtils;
26 import com.android.phone.R;
27 import com.android.phone.SubscriptionInfoHelper;
28 import com.android.services.telephony.sip.SipAccountRegistry;
29 import com.android.services.telephony.sip.SipSharedPreferences;
30 import com.android.services.telephony.sip.SipUtil;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.Iterator;
36 import java.util.List;
37 
38 public class PhoneAccountSettingsFragment extends PreferenceFragment
39         implements Preference.OnPreferenceChangeListener,
40                 AccountSelectionPreference.AccountSelectionListener {
41 
42     private static final String ACCOUNTS_LIST_CATEGORY_KEY =
43             "phone_accounts_accounts_list_category_key";
44 
45     private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
46     private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_account_all_calling_accounts";
47 
48     private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
49             "phone_accounts_sip_settings_category_key";
50     private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
51     private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
52 
53     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
54             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
55 
56     /**
57      * Value to start ordering of phone accounts relative to other preferences. By setting this
58      * value on the phone account listings, we ensure that anything that is ordered before
59      * {value} in the preference XML comes before the phone account list and anything with
60      * a value significantly larger will list after.
61      */
62     private static final int ACCOUNT_ORDERING_START_VALUE = 100;
63 
64     private static final String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
65 
66     private TelecomManager mTelecomManager;
67     private TelephonyManager mTelephonyManager;
68     private SubscriptionManager mSubscriptionManager;
69 
70     private PreferenceCategory mAccountList;
71 
72     private AccountSelectionPreference mDefaultOutgoingAccount;
73 
74     private ListPreference mUseSipCalling;
75     private CheckBoxPreference mSipReceiveCallsPreference;
76     private SipSharedPreferences mSipSharedPreferences;
77 
78     @Override
onCreate(Bundle icicle)79     public void onCreate(Bundle icicle) {
80         super.onCreate(icicle);
81 
82         mTelecomManager = TelecomManager.from(getActivity());
83         mTelephonyManager = TelephonyManager.from(getActivity());
84         mSubscriptionManager = SubscriptionManager.from(getActivity());
85     }
86 
87     @Override
onResume()88     public void onResume() {
89         super.onResume();
90 
91         if (getPreferenceScreen() != null) {
92             getPreferenceScreen().removeAll();
93         }
94 
95         addPreferencesFromResource(R.xml.phone_account_settings);
96 
97         /**
98          * Here we make decisions about what we will and will not display with regards to phone-
99          * account settings.  The basic settings structure is this:
100          * (1) <Make Calls With...>  // Lets user pick a default account for outgoing calls
101          * (2) <Account List>
102          *       <Account>
103          *       ...
104          *       <Account>
105          *     </Account List>
106          * (3) <All Accounts>  // Lets user enable/disable third-party accounts. SIM-based accounts
107          *                     // are always enabled and so aren't relevant here.
108          *
109          * Here are the rules that we follow:
110          * - (1) is only shown if there are multiple enabled accounts, including SIM accounts.
111          *   This can be 2+ SIM accounts, 2+ third party accounts or any combination.
112          * - (2) The account list only lists (a) enabled third party accounts and (b) SIM-based
113          *   accounts. However, for single-SIM devices, if the only account to show is the
114          *   SIM-based account, we don't show the list at all under the assumption that the user
115          *   already knows about the account.
116          * - (3) Is only shown if there exist any third party accounts.  If none exist, then the
117          *   option is hidden since there is nothing that can be done in it.
118          *
119          * By far, the most common case for users will be the single-SIM device without any
120          * third party accounts. IOW, the great majority of users won't see any of these options.
121          */
122         mAccountList = (PreferenceCategory) getPreferenceScreen().findPreference(
123                 ACCOUNTS_LIST_CATEGORY_KEY);
124         List<PhoneAccountHandle> allNonSimAccounts =
125                 getCallingAccounts(false /* includeSims */, true /* includeDisabled */);
126         // Check to see if we should show the entire section at all.
127         if (shouldShowConnectionServiceList(allNonSimAccounts)) {
128             List<PhoneAccountHandle> enabledAccounts =
129                     getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
130             // Initialize the account list with the set of enabled & SIM accounts.
131             initAccountList(enabledAccounts);
132 
133             mDefaultOutgoingAccount = (AccountSelectionPreference)
134                     getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
135             mDefaultOutgoingAccount.setListener(this);
136 
137             // Only show the 'Make Calls With..." option if there are multiple accounts.
138             if (enabledAccounts.size() > 1) {
139                 updateDefaultOutgoingAccountsModel();
140             } else {
141                 mAccountList.removePreference(mDefaultOutgoingAccount);
142             }
143 
144             Preference allAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
145             // If there are no third party (nonSim) accounts, then don't show enable/disable dialog.
146             if (allNonSimAccounts.isEmpty() && allAccounts != null) {
147                 mAccountList.removePreference(allAccounts);
148             }
149         } else {
150             getPreferenceScreen().removePreference(mAccountList);
151         }
152 
153         if (SipUtil.isVoipSupported(getActivity())) {
154             mSipSharedPreferences = new SipSharedPreferences(getActivity());
155 
156             mUseSipCalling = (ListPreference)
157                     getPreferenceScreen().findPreference(USE_SIP_PREF_KEY);
158             mUseSipCalling.setEntries(!SipManager.isSipWifiOnly(getActivity())
159                     ? R.array.sip_call_options_wifi_only_entries
160                     : R.array.sip_call_options_entries);
161             mUseSipCalling.setOnPreferenceChangeListener(this);
162 
163             int optionsValueIndex =
164                     mUseSipCalling.findIndexOfValue(mSipSharedPreferences.getSipCallOption());
165             if (optionsValueIndex == -1) {
166                 // If the option is invalid (eg. deprecated value), default to SIP_ADDRESS_ONLY.
167                 mSipSharedPreferences.setSipCallOption(
168                         getResources().getString(R.string.sip_address_only));
169                 optionsValueIndex =
170                         mUseSipCalling.findIndexOfValue(mSipSharedPreferences.getSipCallOption());
171             }
172             mUseSipCalling.setValueIndex(optionsValueIndex);
173             mUseSipCalling.setSummary(mUseSipCalling.getEntry());
174 
175             mSipReceiveCallsPreference = (CheckBoxPreference)
176                     getPreferenceScreen().findPreference(SIP_RECEIVE_CALLS_PREF_KEY);
177             mSipReceiveCallsPreference.setEnabled(SipUtil.isPhoneIdle(getActivity()));
178             mSipReceiveCallsPreference.setChecked(
179                     mSipSharedPreferences.isReceivingCallsEnabled());
180             mSipReceiveCallsPreference.setOnPreferenceChangeListener(this);
181         } else {
182             getPreferenceScreen().removePreference(
183                     getPreferenceScreen().findPreference(SIP_SETTINGS_CATEGORY_PREF_KEY));
184         }
185     }
186 
187     /**
188      * Handles changes to the preferences.
189      *
190      * @param pref The preference changed.
191      * @param objValue The changed value.
192      * @return True if the preference change has been handled, and false otherwise.
193      */
194     @Override
onPreferenceChange(Preference pref, Object objValue)195     public boolean onPreferenceChange(Preference pref, Object objValue) {
196         if (pref == mUseSipCalling) {
197             String option = objValue.toString();
198             mSipSharedPreferences.setSipCallOption(option);
199             mUseSipCalling.setValueIndex(mUseSipCalling.findIndexOfValue(option));
200             mUseSipCalling.setSummary(mUseSipCalling.getEntry());
201             return true;
202         } else if (pref == mSipReceiveCallsPreference) {
203             final boolean isEnabled = !mSipReceiveCallsPreference.isChecked();
204             new Thread(new Runnable() {
205                 public void run() {
206                     handleSipReceiveCallsOption(isEnabled);
207                 }
208             }).start();
209             return true;
210         }
211         return false;
212     }
213 
214     /**
215      * Handles a phone account selection for the default outgoing phone account.
216      *
217      * @param pref The account selection preference which triggered the account selected event.
218      * @param account The account selected.
219      * @return True if the account selection has been handled, and false otherwise.
220      */
221     @Override
onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account)222     public boolean onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account) {
223         if (pref == mDefaultOutgoingAccount) {
224             mTelecomManager.setUserSelectedOutgoingPhoneAccount(account);
225             return true;
226         }
227         return false;
228     }
229 
230     /**
231      * Repopulate the dialog to pick up changes before showing.
232      *
233      * @param pref The account selection preference dialog being shown.
234      */
235     @Override
onAccountSelectionDialogShow(AccountSelectionPreference pref)236     public void onAccountSelectionDialogShow(AccountSelectionPreference pref) {
237         if (pref == mDefaultOutgoingAccount) {
238             updateDefaultOutgoingAccountsModel();
239         }
240     }
241 
242     @Override
onAccountChanged(AccountSelectionPreference pref)243     public void onAccountChanged(AccountSelectionPreference pref) {}
244 
handleSipReceiveCallsOption(boolean isEnabled)245     private synchronized void handleSipReceiveCallsOption(boolean isEnabled) {
246         Context context = getActivity();
247         if (context == null) {
248             // Return if the fragment is detached from parent activity before executed by thread.
249             return;
250         }
251 
252         mSipSharedPreferences.setReceivingCallsEnabled(isEnabled);
253 
254         SipUtil.useSipToReceiveIncomingCalls(context, isEnabled);
255 
256         // Restart all Sip services to ensure we reflect whether we are receiving calls.
257         SipAccountRegistry sipAccountRegistry = SipAccountRegistry.getInstance();
258         sipAccountRegistry.restartSipService(context);
259     }
260 
261     /**
262      * Queries the telcomm manager to update the default outgoing account selection preference
263      * with the list of outgoing accounts and the current default outgoing account.
264      */
updateDefaultOutgoingAccountsModel()265     private void updateDefaultOutgoingAccountsModel() {
266         mDefaultOutgoingAccount.setModel(
267                 mTelecomManager,
268                 getCallingAccounts(true /* includeSims */, false /* includeDisabled */),
269                 mTelecomManager.getUserSelectedOutgoingPhoneAccount(),
270                 getString(R.string.phone_accounts_ask_every_time));
271     }
272 
initAccountList(List<PhoneAccountHandle> enabledAccounts)273     private void initAccountList(List<PhoneAccountHandle> enabledAccounts) {
274 
275         boolean isMultiSimDevice = mTelephonyManager.isMultiSimEnabled();
276 
277         // On a single-SIM device, do not list any accounts if the only account is the SIM-based
278         // one. This is because on single-SIM devices, we do not expose SIM settings through the
279         // account listing entry so showing it does nothing to help the user. Nor does the lack of
280         // action match the "Settings" header above the listing.
281         if (!isMultiSimDevice && getCallingAccounts(
282                 false /* includeSims */, false /* includeDisabled */).isEmpty()){
283             return;
284         }
285 
286         // Obtain the list of phone accounts.
287         List<PhoneAccount> accounts = new ArrayList<>();
288         for (PhoneAccountHandle handle : enabledAccounts) {
289             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
290             if (account != null) {
291                 accounts.add(account);
292             }
293         }
294 
295         // Sort the accounts according to how we want to display them.
296         Collections.sort(accounts, new Comparator<PhoneAccount>() {
297             @Override
298             public int compare(PhoneAccount account1, PhoneAccount account2) {
299                 int retval = 0;
300 
301                 // SIM accounts go first
302                 boolean isSim1 = account1.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
303                 boolean isSim2 = account2.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
304                 if (isSim1 != isSim2) {
305                     retval = isSim1 ? -1 : 1;
306                 }
307 
308                 // Then order by package
309                 if (retval == 0) {
310                     String pkg1 = account1.getAccountHandle().getComponentName().getPackageName();
311                     String pkg2 = account2.getAccountHandle().getComponentName().getPackageName();
312                     retval = pkg1.compareTo(pkg2);
313                 }
314 
315                 // Finally, order by label
316                 if (retval == 0) {
317                     String label1 = nullToEmpty(account1.getLabel().toString());
318                     String label2 = nullToEmpty(account2.getLabel().toString());
319                     retval = label1.compareTo(label2);
320                 }
321 
322                 // Then by hashcode
323                 if (retval == 0) {
324                     retval = account1.hashCode() - account2.hashCode();
325                 }
326                 return retval;
327             }
328         });
329 
330         int order = ACCOUNT_ORDERING_START_VALUE;
331 
332         // Add an entry for each account.
333         for (PhoneAccount account : accounts) {
334             PhoneAccountHandle handle = account.getAccountHandle();
335             Intent intent = null;
336 
337             // SIM phone accounts use a different setting intent and are thus handled differently.
338             if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
339 
340                 // For SIM-based accounts, we only expose the settings through the account list
341                 // if we are on a multi-SIM device. For single-SIM devices, the settings are
342                 // more spread out so there is no good single place to take the user, so we don't.
343                 if (isMultiSimDevice) {
344                     SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
345                             mTelephonyManager.getSubIdForPhoneAccount(account));
346 
347                     if (subInfo != null) {
348                         intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
349                         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
350                         SubscriptionInfoHelper.addExtrasToIntent(intent, subInfo);
351                     }
352                 }
353             } else {
354                 intent = buildPhoneAccountConfigureIntent(getActivity(), handle);
355             }
356 
357             // Create the preference & add the label
358             Preference accountPreference = new Preference(getActivity());
359             accountPreference.setTitle(account.getLabel());
360 
361             // Add an icon.
362             Icon icon = account.getIcon();
363             if (icon != null) {
364                 accountPreference.setIcon(icon.loadDrawable(getActivity()));
365             }
366 
367             // Add an intent to send the user to the account's settings.
368             if (intent != null) {
369                 accountPreference.setIntent(intent);
370             }
371 
372             accountPreference.setOrder(order++);
373             mAccountList.addPreference(accountPreference);
374         }
375     }
376 
shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts)377     private boolean shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts) {
378         return mTelephonyManager.isMultiSimEnabled() || allNonSimAccounts.size() > 0;
379     }
380 
getCallingAccounts( boolean includeSims, boolean includeDisabledAccounts)381     private List<PhoneAccountHandle> getCallingAccounts(
382             boolean includeSims, boolean includeDisabledAccounts) {
383         PhoneAccountHandle emergencyAccountHandle = getEmergencyPhoneAccount();
384 
385         List<PhoneAccountHandle> accountHandles =
386                 mTelecomManager.getCallCapablePhoneAccounts(includeDisabledAccounts);
387         for (Iterator<PhoneAccountHandle> i = accountHandles.iterator(); i.hasNext();) {
388             PhoneAccountHandle handle = i.next();
389             if (handle.equals(emergencyAccountHandle)) {
390                 // never include emergency call accounts in this piece of code.
391                 i.remove();
392                 continue;
393             }
394 
395             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
396             if (account == null) {
397                 i.remove();
398             } else if (!includeSims &&
399                     account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
400                 i.remove();
401             }
402         }
403         return accountHandles;
404     }
405 
nullToEmpty(String str)406     private String nullToEmpty(String str) {
407         return str == null ? "" : str;
408     }
409 
getEmergencyPhoneAccount()410     private PhoneAccountHandle getEmergencyPhoneAccount() {
411         return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
412                 (Phone) null, "" /* prefix */, true /* isEmergency */);
413     }
414 
buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)415     public static Intent buildPhoneAccountConfigureIntent(
416             Context context, PhoneAccountHandle accountHandle) {
417         Intent intent = buildConfigureIntent(
418                 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
419 
420         if (intent == null) {
421             // If the new configuration didn't work, try the old configuration intent.
422             intent = buildConfigureIntent(
423                     context, accountHandle, LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
424             if (intent != null) {
425                 Log.w(LOG_TAG, "Phone account using old configuration intent: " + accountHandle);
426             }
427         }
428         return intent;
429     }
430 
buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)431     private static Intent buildConfigureIntent(
432             Context context, PhoneAccountHandle accountHandle, String actionStr) {
433         if (accountHandle == null || accountHandle.getComponentName() == null ||
434                 TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
435             return null;
436         }
437 
438         // Build the settings intent.
439         Intent intent = new Intent(actionStr);
440         intent.setPackage(accountHandle.getComponentName().getPackageName());
441         intent.addCategory(Intent.CATEGORY_DEFAULT);
442         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
443 
444         // Check to see that the phone account package can handle the setting intent.
445         PackageManager pm = context.getPackageManager();
446         List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
447         if (resolutions.size() == 0) {
448             intent = null;  // set no intent if the package cannot handle it.
449         }
450 
451         return intent;
452     }
453 }
454