[Provider model] Internet picker Part II

- Add mobile internet preference with default data subscription.
 - Make prefrence to show dynamically when data subscription changed.

Video exhibition
 - http://rcll/gcmsphfhJ1UzfPSvjWOuXK/c1B2CtaFz27rlIQ2LSTJmo

Code difference
 - SubscriptionsPreferenceControllerTest.java between robolectric and junit.
  - https://diff.googleplex.com/#key=1Zm7JGPhoZwY

Bug: 172229552
Test: atest SubscriptionsPreferenceControllerTest

Change-Id: Ib50c2c51159f60f19631d1a02081eafde3436e94
This commit is contained in:
tom hsu
2020-12-01 14:42:19 +08:00
parent 2133856853
commit 65f8aa2f7b
3 changed files with 867 additions and 580 deletions

View File

@@ -21,10 +21,13 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -46,6 +49,7 @@ import com.android.settings.network.telephony.DataConnectivityListener;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.SignalStrengthListener;
import com.android.settings.widget.GearPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.net.SignalStrengthUtil;
@@ -55,9 +59,15 @@ import java.util.Map;
import java.util.Set;
/**
* This manages a set of Preferences it places into a PreferenceGroup owned by some parent
* If the provider model is not enabled, this controller manages a set of Preferences it places into
* a PreferenceGroup owned by some parent
* controller class - one for each available subscription. This controller is only considered
* available if there are 2 or more subscriptions.
*
* If the provider model is enabled, this controller manages preference with data subscription
* information and make its state display on preference.
* TODO this class will clean up the multiple subscriptions functionality after the provider
* model is released.
*/
public class SubscriptionsPreferenceController extends AbstractPreferenceController implements
LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient,
@@ -68,16 +78,30 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
private UpdateListener mUpdateListener;
private String mPreferenceGroupKey;
private PreferenceGroup mPreferenceGroup;
private SubscriptionManager mManager;
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
private SubscriptionsChangeListener mSubscriptionsListener;
private MobileDataEnabledListener mDataEnabledListener;
private DataConnectivityListener mConnectivityListener;
private SignalStrengthListener mSignalStrengthListener;
@VisibleForTesting
final BroadcastReceiver mDataSubscriptionChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
update();
}
}
};
// Map of subscription id to Preference
private Map<Integer, Preference> mSubscriptionPreferences;
private int mStartOrder;
private GearPreference mSubsGearPref;
private SubsPrefCtrlInjector mSubsPrefCtrlInjector;
/**
* This interface lets a parent of this class know that some change happened - this could
* either be because overall availability changed, or because we've added/removed/updated some
@@ -107,21 +131,37 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mUpdateListener = updateListener;
mPreferenceGroupKey = preferenceGroupKey;
mStartOrder = startOrder;
mManager = context.getSystemService(SubscriptionManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mSubscriptionPreferences = new ArrayMap<>();
mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
mDataEnabledListener = new MobileDataEnabledListener(context, this);
mConnectivityListener = new DataConnectivityListener(context, this);
mSignalStrengthListener = new SignalStrengthListener(context, this);
lifecycle.addObserver(this);
mSubsPrefCtrlInjector = createSubsPrefCtrlInjector();
}
private void registerDataSubscriptionChangedReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
mContext.registerReceiver(mDataSubscriptionChangedReceiver, filter);
}
private void unRegisterDataSubscriptionChangedReceiver() {
if (mDataSubscriptionChangedReceiver != null) {
mContext.unregisterReceiver(mDataSubscriptionChangedReceiver);
}
}
@OnLifecycleEvent(ON_RESUME)
public void onResume() {
mSubscriptionsListener.start();
mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
mDataEnabledListener.start(mSubsPrefCtrlInjector.getDefaultDataSubscriptionId());
mConnectivityListener.start();
mSignalStrengthListener.resume();
registerDataSubscriptionChangedReceiver();
update();
}
@@ -131,6 +171,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mDataEnabledListener.stop();
mConnectivityListener.stop();
mSignalStrengthListener.pause();
unRegisterDataSubscriptionChangedReceiver();
}
@Override
@@ -143,29 +184,116 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
if (mPreferenceGroup == null) {
return;
}
if (!isAvailable()) {
if (mSubsGearPref != null) {
mPreferenceGroup.removePreference(mSubsGearPref);
}
for (Preference pref : mSubscriptionPreferences.values()) {
mPreferenceGroup.removePreference(pref);
}
mSubscriptionPreferences.clear();
mSignalStrengthListener.updateSubscriptionIds(Collections.emptySet());
mUpdateListener.onChildrenUpdated();
return;
}
if (mSubsPrefCtrlInjector.isProviderModelEnabled(mContext)) {
updateForProvider();
} else {
updateForBase();
}
}
private void updateForProvider() {
SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
if (subInfo == null) {
mPreferenceGroup.removeAll();
return;
}
if (mSubsGearPref == null) {
mPreferenceGroup.removeAll();
mSubsGearPref = new GearPreference(mContext, null);
mSubsGearPref.setOnPreferenceClickListener(preference -> {
//TODO(b/176141379) Wait for wifiManager#selectCarrier(int subscriptionId)
return true;
});
mSubsGearPref.setOnGearClickListener(p ->
startMobileNetworkActivity(mContext, subInfo.getSubscriptionId()));
}
mSubsGearPref.setTitle(subInfo.getDisplayName());
mSubsGearPref.setOrder(mStartOrder);
//TODO(b/176141828) Wait for api provided by system ui.
mSubsGearPref.setSummary(getMobilePreferenceSummary());
mSubsGearPref.setIcon(getIcon(subInfo.getSubscriptionId()));
mPreferenceGroup.addPreference(mSubsGearPref);
final Set<Integer> activeDataSubIds = new ArraySet<>();
activeDataSubIds.add(subInfo.getSubscriptionId());
mSignalStrengthListener.updateSubscriptionIds(activeDataSubIds);
mUpdateListener.onChildrenUpdated();
}
private String getMobilePreferenceSummary() {
//TODO(b/176141828) Waiting for the api provided by system UI.
String result = "5G";
if (MobileNetworkUtils.activeNetworkIsCellular(mContext)) {
result = "Active, " + result;
}
return result;
}
private Drawable getIcon(int subId) {
final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId);
final SignalStrength strength = tmForSubId.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
if (shouldInflateSignalStrength(subId)) {
level += 1;
numLevels += 1;
}
final boolean isMobileDataOn = tmForSubId.isDataEnabled();
final boolean isActiveCellularNetwork =
mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext);
final boolean isMobileDataAccessible = tmForSubId.getDataState()
== TelephonyManager.DATA_CONNECTED;
final ServiceState serviceState = tmForSubId.getServiceState();
final boolean isVoiceOutOfService = (serviceState == null)
? true
: (serviceState.getState() == ServiceState.STATE_OUT_OF_SERVICE);
Drawable icon = mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, false);
if (isActiveCellularNetwork) {
icon.setTint(Utils.getColorAccentDefaultColor(mContext));
return icon;
}
if ((isMobileDataOn && isMobileDataAccessible)
|| (!isMobileDataOn && !isVoiceOutOfService)) {
return icon;
}
icon = mContext.getDrawable(R.drawable.ic_signal_strength_zero_bar_no_internet);
return icon;
}
private void updateForBase() {
final Map<Integer, Preference> existingPrefs = mSubscriptionPreferences;
mSubscriptionPreferences = new ArrayMap<>();
int order = mStartOrder;
final Set<Integer> activeSubIds = new ArraySet<>();
final int dataDefaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
for (SubscriptionInfo info : SubscriptionUtil.getActiveSubscriptions(mManager)) {
final int dataDefaultSubId = mSubsPrefCtrlInjector.getDefaultDataSubscriptionId();
for (SubscriptionInfo info :
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager)) {
final int subId = info.getSubscriptionId();
// Avoid from showing subscription(SIM)s which has been marked as hidden
// For example, only one subscription will be shown when there're multiple
// subscriptions with same group UUID.
if (!canSubscriptionBeDisplayed(mContext, subId)) {
if (!mSubsPrefCtrlInjector.canSubscriptionBeDisplayed(mContext, subId)) {
continue;
}
activeSubIds.add(subId);
@@ -181,9 +309,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
pref.setOrder(order++);
pref.setOnPreferenceClickListener(clickedPref -> {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
intent.putExtra(Settings.EXTRA_SUB_ID, subId);
mContext.startActivity(intent);
startMobileNetworkActivity(mContext, subId);
return true;
});
@@ -198,6 +324,12 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mUpdateListener.onChildrenUpdated();
}
private static void startMobileNetworkActivity(Context context, int subId) {
final Intent intent = new Intent(context, MobileNetworkActivity.class);
intent.putExtra(Settings.EXTRA_SUB_ID, subId);
context.startActivity(intent);
}
@VisibleForTesting
boolean shouldInflateSignalStrength(int subId) {
return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
@@ -214,14 +346,9 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
level += 1;
numLevels += 1;
}
final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled();
pref.setIcon(getIcon(level, numLevels, showCutOut));
}
@VisibleForTesting
Drawable getIcon(int level, int numLevels, boolean cutOut) {
return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels,
NO_CELL_DATA_TYPE_ICON, cutOut);
final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled();
pref.setIcon(mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, showCutOut));
}
/**
@@ -236,8 +363,8 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
* If a subscription isn't the default for anything, we just say it is available.
*/
protected String getSummary(int subId, boolean isDefaultForData) {
final int callsDefaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
final int smsDefaultSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
final int callsDefaultSubId = mSubsPrefCtrlInjector.getDefaultVoiceSubscriptionId();
final int smsDefaultSubId = mSubsPrefCtrlInjector.getDefaultSmsSubscriptionId();
String line1 = null;
if (subId == callsDefaultSubId && subId == smsDefaultSubId) {
@@ -253,7 +380,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
final TelephonyManager telMgrForSub = mContext.getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
final boolean dataEnabled = telMgrForSub.isDataEnabled();
if (dataEnabled && MobileNetworkUtils.activeNetworkIsCellular(mContext)) {
if (dataEnabled && mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext)) {
line2 = mContext.getString(R.string.mobile_data_active);
} else if (!dataEnabled) {
line2 = mContext.getString(R.string.mobile_data_off);
@@ -274,14 +401,16 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
}
/**
* @return true if there are at least 2 available subscriptions.
* @return true if there are at least 2 available subscriptions,
* or if there is at least 1 available subscription for provider model.
*/
@Override
public boolean isAvailable() {
if (mSubscriptionsListener.isAirplaneModeOn()) {
return false;
}
List<SubscriptionInfo> subInfoList = SubscriptionUtil.getActiveSubscriptions(mManager);
List<SubscriptionInfo> subInfoList =
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
if (subInfoList == null) {
return false;
}
@@ -290,8 +419,9 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
// For example, only one subscription will be shown when there're multiple
// subscriptions with same group UUID.
.filter(subInfo ->
canSubscriptionBeDisplayed(mContext, subInfo.getSubscriptionId()))
.count() >= (Utils.isProviderModelEnabled(mContext) ? 1 : 2);
mSubsPrefCtrlInjector.canSubscriptionBeDisplayed(mContext,
subInfo.getSubscriptionId()))
.count() >= (mSubsPrefCtrlInjector.isProviderModelEnabled(mContext) ? 1 : 2);
}
@Override
@@ -307,7 +437,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
@Override
public void onSubscriptionsChanged() {
// See if we need to change which sub id we're using to listen for enabled/disabled changes.
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
int defaultDataSubId = mSubsPrefCtrlInjector.getDefaultDataSubscriptionId();
if (defaultDataSubId != mDataEnabledListener.getSubId()) {
mDataEnabledListener.stop();
mDataEnabledListener.start(defaultDataSubId);
@@ -335,4 +465,65 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
return (SubscriptionUtil.getAvailableSubscription(context,
ProxySubscriptionManager.getInstance(context), subId) != null);
}
}
SubsPrefCtrlInjector createSubsPrefCtrlInjector() {
return new SubsPrefCtrlInjector();
}
/**
* To inject necessary data from each static api.
*/
@VisibleForTesting
public static class SubsPrefCtrlInjector {
/**
* Use to inject function and value for class and test class.
*/
public boolean canSubscriptionBeDisplayed(Context context, int subId) {
return (SubscriptionUtil.getAvailableSubscription(context,
ProxySubscriptionManager.getInstance(context), subId) != null);
}
/**
* Check SIM be able to display on UI.
*/
public int getDefaultSmsSubscriptionId() {
return SubscriptionManager.getDefaultSmsSubscriptionId();
}
/**
* Get default voice subscription ID.
*/
public int getDefaultVoiceSubscriptionId() {
return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
/**
* Get default data subscription ID.
*/
public int getDefaultDataSubscriptionId() {
return SubscriptionManager.getDefaultDataSubscriptionId();
}
/**
* Confirm the current network is cellular and active.
*/
public boolean isActiveCellularNetwork(Context context) {
return MobileNetworkUtils.activeNetworkIsCellular(context);
}
/**
* Confirm the flag of Provider Model switch is turned on or not.
*/
public boolean isProviderModelEnabled(Context context) {
return Utils.isProviderModelEnabled(context);
}
/**
* Get signal icon with different signal level.
*/
public Drawable getIcon(Context context, int level, int numLevels, boolean cutOut) {
return MobileNetworkUtils.getSignalStrengthIcon(context, level, numLevels,
NO_CELL_DATA_TYPE_ICON, cutOut);
}
}
}