blob: ca45b31ba898a6985df4d6caa9800ff4efc22fc9 [file] [log] [blame]
package com.android.phone.settings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.sip.SipManager;
import android.os.Bundle;
import android.os.UserManager;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.SwitchPreference;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.Phone;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
import com.android.phone.SubscriptionInfoHelper;
import com.android.services.telephony.sip.SipAccountRegistry;
import com.android.services.telephony.sip.SipPreferences;
import com.android.services.telephony.sip.SipUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class PhoneAccountSettingsFragment extends PreferenceFragment
implements Preference.OnPreferenceChangeListener,
AccountSelectionPreference.AccountSelectionListener {
private static final String ACCOUNTS_LIST_CATEGORY_KEY =
"phone_accounts_accounts_list_category_key";
private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_accounts_all_calling_accounts";
private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
"phone_accounts_sip_settings_category_key";
private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
"android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
/**
* Value to start ordering of phone accounts relative to other preferences. By setting this
* value on the phone account listings, we ensure that anything that is ordered before
* {value} in the preference XML comes before the phone account list and anything with
* a value significantly larger will list after.
*/
private static final int ACCOUNT_ORDERING_START_VALUE = 100;
private static final String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
private TelecomManager mTelecomManager;
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
private PreferenceCategory mAccountList;
private AccountSelectionPreference mDefaultOutgoingAccount;
private Preference mAllCallingAccounts;
private ListPreference mUseSipCalling;
private SwitchPreference mSipReceiveCallsPreference;
private SipPreferences mSipPreferences;
private final SubscriptionManager.OnSubscriptionsChangedListener
mOnSubscriptionsChangeListener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
updateAccounts();
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mTelecomManager = TelecomManager.from(getActivity());
mTelephonyManager = TelephonyManager.from(getActivity());
mSubscriptionManager = SubscriptionManager.from(getActivity());
}
@Override
public void onResume() {
super.onResume();
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
}
addPreferencesFromResource(R.xml.phone_account_settings);
/**
* Here we make decisions about what we will and will not display with regards to phone-
* account settings. The basic settings structure is this:
* (1) <Make Calls With...> // Lets user pick a default account for outgoing calls
* (2) <Account List>
* <Account>
* ...
* <Account>
* </Account List>
* (3) <All Accounts> // Lets user enable/disable third-party accounts. SIM-based accounts
* // are always enabled and so aren't relevant here.
*
* Here are the rules that we follow:
* - (1) is only shown if there are multiple enabled accounts, including SIM accounts.
* This can be 2+ SIM accounts, 2+ third party accounts or any combination.
* - (2) The account list only lists (a) enabled third party accounts and (b) SIM-based
* accounts. However, for single-SIM devices, if the only account to show is the
* SIM-based account, we don't show the list at all under the assumption that the user
* already knows about the account.
* - (3) Is only shown if there exist any third party accounts. If none exist, then the
* option is hidden since there is nothing that can be done in it.
*
* By far, the most common case for users will be the single-SIM device without any
* third party accounts. IOW, the great majority of users won't see any of these options.
*/
mAccountList = (PreferenceCategory) getPreferenceScreen().findPreference(
ACCOUNTS_LIST_CATEGORY_KEY);
mDefaultOutgoingAccount = (AccountSelectionPreference)
getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
mAllCallingAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
updateAccounts();
if (isPrimaryUser() && SipUtil.isVoipSupported(getActivity())) {
mSipPreferences = new SipPreferences(getActivity());
mUseSipCalling = (ListPreference)
getPreferenceScreen().findPreference(USE_SIP_PREF_KEY);
mUseSipCalling.setEntries(!SipManager.isSipWifiOnly(getActivity())
? R.array.sip_call_options_wifi_only_entries
: R.array.sip_call_options_entries);
mUseSipCalling.setOnPreferenceChangeListener(this);
int optionsValueIndex =
mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
if (optionsValueIndex == -1) {
// If the option is invalid (eg. deprecated value), default to SIP_ADDRESS_ONLY.
mSipPreferences.setSipCallOption(
getResources().getString(R.string.sip_address_only));
optionsValueIndex =
mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
}
mUseSipCalling.setValueIndex(optionsValueIndex);
mUseSipCalling.setSummary(mUseSipCalling.getEntry());
mSipReceiveCallsPreference = (SwitchPreference)
getPreferenceScreen().findPreference(SIP_RECEIVE_CALLS_PREF_KEY);
mSipReceiveCallsPreference.setEnabled(SipUtil.isPhoneIdle(getActivity()));
mSipReceiveCallsPreference.setChecked(
mSipPreferences.isReceivingCallsEnabled());
mSipReceiveCallsPreference.setOnPreferenceChangeListener(this);
} else {
getPreferenceScreen().removePreference(
getPreferenceScreen().findPreference(SIP_SETTINGS_CATEGORY_PREF_KEY));
}
SubscriptionManager.from(getActivity()).addOnSubscriptionsChangedListener(
mOnSubscriptionsChangeListener);
}
@Override
public void onPause() {
SubscriptionManager.from(getActivity()).removeOnSubscriptionsChangedListener(
mOnSubscriptionsChangeListener);
super.onPause();
}
/**
* Handles changes to the preferences.
*
* @param pref The preference changed.
* @param objValue The changed value.
* @return True if the preference change has been handled, and false otherwise.
*/
@Override
public boolean onPreferenceChange(Preference pref, Object objValue) {
if (pref == mUseSipCalling) {
String option = objValue.toString();
mSipPreferences.setSipCallOption(option);
mUseSipCalling.setValueIndex(mUseSipCalling.findIndexOfValue(option));
mUseSipCalling.setSummary(mUseSipCalling.getEntry());
return true;
} else if (pref == mSipReceiveCallsPreference) {
final boolean isEnabled = !mSipReceiveCallsPreference.isChecked();
new Thread(new Runnable() {
public void run() {
handleSipReceiveCallsOption(isEnabled);
}
}).start();
return true;
}
return false;
}
/**
* Handles a phone account selection for the default outgoing phone account.
*
* @param pref The account selection preference which triggered the account selected event.
* @param account The account selected.
* @return True if the account selection has been handled, and false otherwise.
*/
@Override
public boolean onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account) {
if (pref == mDefaultOutgoingAccount) {
mTelecomManager.setUserSelectedOutgoingPhoneAccount(account);
return true;
}
return false;
}
/**
* Repopulate the dialog to pick up changes before showing.
*
* @param pref The account selection preference dialog being shown.
*/
@Override
public void onAccountSelectionDialogShow(AccountSelectionPreference pref) {
if (pref == mDefaultOutgoingAccount) {
updateDefaultOutgoingAccountsModel();
}
}
@Override
public void onAccountChanged(AccountSelectionPreference pref) {}
private synchronized void handleSipReceiveCallsOption(boolean isEnabled) {
Context context = getActivity();
if (context == null) {
// Return if the fragment is detached from parent activity before executed by thread.
return;
}
mSipPreferences.setReceivingCallsEnabled(isEnabled);
SipUtil.useSipToReceiveIncomingCalls(context, isEnabled);
// Restart all Sip services to ensure we reflect whether we are receiving calls.
SipAccountRegistry sipAccountRegistry = SipAccountRegistry.getInstance();
sipAccountRegistry.restartSipService(context);
}
/**
* Queries the telcomm manager to update the default outgoing account selection preference
* with the list of outgoing accounts and the current default outgoing account.
*/
private void updateDefaultOutgoingAccountsModel() {
mDefaultOutgoingAccount.setModel(
mTelecomManager,
getCallingAccounts(true /* includeSims */, false /* includeDisabled */),
mTelecomManager.getUserSelectedOutgoingPhoneAccount(),
getString(R.string.phone_accounts_ask_every_time));
}
private void initAccountList(List<PhoneAccountHandle> enabledAccounts) {
boolean isMultiSimDevice = mTelephonyManager.isMultiSimEnabled();
// On a single-SIM device, do not list any accounts if the only account is the SIM-based
// one. This is because on single-SIM devices, we do not expose SIM settings through the
// account listing entry so showing it does nothing to help the user. Nor does the lack of
// action match the "Settings" header above the listing.
if (!isMultiSimDevice && getCallingAccounts(
false /* includeSims */, false /* includeDisabled */).isEmpty()){
return;
}
// Obtain the list of phone accounts.
List<PhoneAccount> accounts = new ArrayList<>();
for (PhoneAccountHandle handle : enabledAccounts) {
PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
if (account != null) {
accounts.add(account);
}
}
// Sort the accounts according to how we want to display them.
Collections.sort(accounts, new Comparator<PhoneAccount>() {
@Override
public int compare(PhoneAccount account1, PhoneAccount account2) {
int retval = 0;
// SIM accounts go first
boolean isSim1 = account1.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
boolean isSim2 = account2.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
if (isSim1 != isSim2) {
retval = isSim1 ? -1 : 1;
}
int subId1 = mTelephonyManager.getSubIdForPhoneAccount(account1);
int subId2 = mTelephonyManager.getSubIdForPhoneAccount(account2);
if (subId1 != SubscriptionManager.INVALID_SUBSCRIPTION_ID &&
subId2 != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
retval = (mSubscriptionManager.getSlotIndex(subId1) <
mSubscriptionManager.getSlotIndex(subId2)) ? -1 : 1;
}
// Then order by package
if (retval == 0) {
String pkg1 = account1.getAccountHandle().getComponentName().getPackageName();
String pkg2 = account2.getAccountHandle().getComponentName().getPackageName();
retval = pkg1.compareTo(pkg2);
}
// Finally, order by label
if (retval == 0) {
String label1 = nullToEmpty(account1.getLabel().toString());
String label2 = nullToEmpty(account2.getLabel().toString());
retval = label1.compareTo(label2);
}
// Then by hashcode
if (retval == 0) {
retval = account1.hashCode() - account2.hashCode();
}
return retval;
}
});
int order = ACCOUNT_ORDERING_START_VALUE;
// Add an entry for each account.
for (PhoneAccount account : accounts) {
PhoneAccountHandle handle = account.getAccountHandle();
Intent intent = null;
// SIM phone accounts use a different setting intent and are thus handled differently.
if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
// For SIM-based accounts, we only expose the settings through the account list
// if we are on a multi-SIM device. For single-SIM devices, the settings are
// more spread out so there is no good single place to take the user, so we don't.
if (isMultiSimDevice) {
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
mTelephonyManager.getSubIdForPhoneAccount(account));
if (subInfo != null) {
intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
SubscriptionInfoHelper.addExtrasToIntent(intent, subInfo);
}
}
} else {
intent = buildPhoneAccountConfigureIntent(getActivity(), handle);
}
// Create the preference & add the label
Preference accountPreference = new Preference(getActivity());
CharSequence accountLabel = account.getLabel();
boolean isSimAccount =
account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
accountPreference.setTitle((TextUtils.isEmpty(accountLabel) && isSimAccount)
? getString(R.string.phone_accounts_default_account_label) : accountLabel);
// Add an icon.
Icon icon = account.getIcon();
if (icon != null) {
accountPreference.setIcon(icon.loadDrawable(getActivity()));
}
// Add an intent to send the user to the account's settings.
if (intent != null) {
accountPreference.setIntent(intent);
}
accountPreference.setOrder(order++);
mAccountList.addPreference(accountPreference);
}
}
private boolean shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts) {
return mTelephonyManager.isMultiSimEnabled() || allNonSimAccounts.size() > 0;
}
private void updateAccounts() {
if (mAccountList != null) {
mAccountList.removeAll();
List<PhoneAccountHandle> allNonSimAccounts =
getCallingAccounts(false /* includeSims */, true /* includeDisabled */);
// Check to see if we should show the entire section at all.
if (shouldShowConnectionServiceList(allNonSimAccounts)) {
List<PhoneAccountHandle> enabledAccounts =
getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
// Initialize the account list with the set of enabled & SIM accounts.
initAccountList(enabledAccounts);
mDefaultOutgoingAccount.setListener(this);
// Only show the 'Make Calls With..." option if there are multiple accounts.
if (enabledAccounts.size() > 1) {
mAccountList.addPreference(mDefaultOutgoingAccount);
updateDefaultOutgoingAccountsModel();
} else {
mAccountList.removePreference(mDefaultOutgoingAccount);
}
// If there are no third party (nonSim) accounts,
// then don't show enable/disable dialog.
if (!allNonSimAccounts.isEmpty()) {
mAccountList.addPreference(mAllCallingAccounts);
} else {
mAccountList.removePreference(mAllCallingAccounts);
}
} else {
getPreferenceScreen().removePreference(mAccountList);
}
}
}
private List<PhoneAccountHandle> getCallingAccounts(
boolean includeSims, boolean includeDisabledAccounts) {
PhoneAccountHandle emergencyAccountHandle = getEmergencyPhoneAccount();
List<PhoneAccountHandle> accountHandles =
mTelecomManager.getCallCapablePhoneAccounts(includeDisabledAccounts);
for (Iterator<PhoneAccountHandle> i = accountHandles.iterator(); i.hasNext();) {
PhoneAccountHandle handle = i.next();
if (handle.equals(emergencyAccountHandle)) {
// never include emergency call accounts in this piece of code.
i.remove();
continue;
}
PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
if (account == null) {
i.remove();
} else if (!includeSims &&
account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
i.remove();
}
}
return accountHandles;
}
private String nullToEmpty(String str) {
return str == null ? "" : str;
}
private PhoneAccountHandle getEmergencyPhoneAccount() {
return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
(Phone) null, "" /* prefix */, true /* isEmergency */);
}
public static Intent buildPhoneAccountConfigureIntent(
Context context, PhoneAccountHandle accountHandle) {
Intent intent = buildConfigureIntent(
context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
if (intent == null) {
// If the new configuration didn't work, try the old configuration intent.
intent = buildConfigureIntent(
context, accountHandle, LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
if (intent != null) {
Log.w(LOG_TAG, "Phone account using old configuration intent: " + accountHandle);
}
}
return intent;
}
private static Intent buildConfigureIntent(
Context context, PhoneAccountHandle accountHandle, String actionStr) {
if (accountHandle == null || accountHandle.getComponentName() == null ||
TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
return null;
}
// Build the settings intent.
Intent intent = new Intent(actionStr);
intent.setPackage(accountHandle.getComponentName().getPackageName());
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
// Check to see that the phone account package can handle the setting intent.
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
if (resolutions.size() == 0) {
intent = null; // set no intent if the package cannot handle it.
}
return intent;
}
/**
* @return Whether the current user is the primary user.
*/
private boolean isPrimaryUser() {
final UserManager userManager = (UserManager) getActivity()
.getSystemService(Context.USER_SERVICE);
return userManager.isPrimaryUser();
}
}