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