Merge changes from topic "Slice of Provider Model"

* changes:
  Slice of Provider Model: add slice structure
  The helper for slice of carrier and non-Carrier, used by ProviderModelSlice.
This commit is contained in:
SongFerng Wang
2020-12-23 18:47:15 +00:00
committed by Android (Google) Code Review
15 changed files with 1716 additions and 8 deletions

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
android:strokeAlpha="0.3"
android:fillAlpha="0.3"/>
<path
android:fillColor="#FF000000"
android:pathData="M20,10h2v8h-2z"/>
<path
android:fillColor="#FF000000"
android:pathData="M20,20h2v2h-2z"/>
</vector>

View File

@@ -12527,6 +12527,15 @@
<!-- Label text to turn off airplane mode. [CHAR LIMIT=40] -->
<string name="turn_off_airplane_mode">Turn off Airplane Mode</string>
<!-- Provider Model: Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_connection_active">Active, <xliff:g id="network_data_rat" example="LTE">%1$s</xliff:g></string>
<!-- Provider Model: Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_off_summary">Internet off</string>
<!-- Provider Model: Summary indicating that non-carrier network unavailable [CHAR LIMIT=50] -->
<string name="non_carrier_network_unavailable">Non\u2011carrier networks unavailable</string>
<!-- Provider Model: Summary indicating that non-carrier and carrier network unavailable [CHAR LIMIT=50] -->
<string name="all_network_unavailable">Networks unavailable</string>
<!-- Summary for preference when Bedtime mode is on [CHAR LIMIT=NONE] -->
<string name="aware_summary_when_bedtime_on">Unavailable because bedtime mode is on</string>

View File

@@ -1156,5 +1156,4 @@ public final class Utils extends com.android.settingslib.Utils {
public static boolean isProviderModelEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
}

View File

@@ -103,8 +103,10 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {
private static final String TAG = "NetworkProviderSettings";
public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
"android.settings.NETWORK_PROVIDER_SETTINGS";
private static final String TAG = "NetworkProviderSettings";
// IDs of context menu
static final int MENU_ID_CONNECT = Menu.FIRST + 1;
@VisibleForTesting

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.telephony.SubscriptionManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.slice.WifiSlice;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import java.util.stream.Collectors;
/**
* {@link CustomSliceable} for Wi-Fi and mobile data connection, used by generic clients.
*/
// ToDo If the provider model become default design in the future, the code needs to refactor
// the whole structure and use new "data object", and then split provider model out of old design.
public class ProviderModelSlice extends WifiSlice {
private static final String TAG = "ProviderModelSlice";
private final ProviderModelSliceHelper mHelper;
public ProviderModelSlice(Context context) {
super(context);
mHelper = getHelper();
}
@Override
public Uri getUri() {
return PROVIDER_MODEL_SLICE_URI;
}
private static void log(String s) {
Log.d(TAG, s);
}
protected boolean isApRowCollapsed() {
return false;
}
@Override
public Slice getSlice() {
// The provider model slice step:
// First section: Add a Wi-Fi item which state is connected.
// Second section: Add a carrier item.
// Third section: Add the Wi-Fi items which are not connected.
// Fourth section: If device has connection problem, this row show the message for user.
if (mHelper.isAirplaneModeEnabled()) {
log("Airplane mode is enabled.");
// ToDo Next CL will add the Airplane mode Message.
return mHelper.createListBuilder(getUri()).build();
}
int maxListSize = 0;
List<WifiSliceItem> wifiList = null;
final NetworkProviderWorker worker = getWorker();
if (worker != null) {
// get Wi-Fi list.
wifiList = worker.getResults();
maxListSize = worker.getApRowCount();
} else {
log("network provider worker is null.");
}
final boolean hasCarrier = mHelper.hasCarrier();
log("hasCarrier: " + hasCarrier);
final ListBuilder listBuilder = mHelper.createListBuilder(getUri());
// First section: Add a Wi-Fi item which state is connected.
final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
if (connectedWifiItem != null) {
log("get Wi-Fi item witch is connected");
listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
maxListSize--;
}
// Second section: Add a carrier item.
if (hasCarrier) {
listBuilder.addRow(mHelper.createCarrierRow());
maxListSize--;
}
// Third section: Add the Wi-Fi items which are not connected.
if (wifiList != null) {
log("get Wi-Fi items which are not connected");
final List<WifiSliceItem> disconnectedWifiList = wifiList.stream()
.filter(wifiSliceItem -> wifiSliceItem.getConnectedState()
!= WifiEntry.CONNECTED_STATE_CONNECTED)
.limit(maxListSize)
.collect(Collectors.toList());
for (WifiSliceItem item : disconnectedWifiList) {
listBuilder.addRow(getWifiSliceItemRow(item));
}
}
// Fourth section: If device has connection problem, this row show the message for user.
// 1) show non_carrier_network_unavailable:
// - while no wifi item
// 2) show all_network_unavailable:
// - while no wifi item + no carrier
// - while no wifi item + no data capability
if (worker == null || wifiList == null) {
log("wifiList is null");
int resId = R.string.non_carrier_network_unavailable;
if (!hasCarrier || mHelper.isNoCarrierData()) {
log("No carrier item or no carrier data.");
resId = R.string.all_network_unavailable;
}
if (!hasCarrier) {
// If there is no item in ProviderModelItem, slice needs a header.
listBuilder.setHeader(mHelper.createHeader());
}
listBuilder.addGridRow(mHelper.createMessageGridRow(resId));
}
return listBuilder.build();
}
/**
* Update the current carrier's mobile data status.
*/
@Override
public void onNotifyChange(Intent intent) {
final SubscriptionManager subscriptionManager = mHelper.getSubscriptionManager();
if (subscriptionManager == null) {
return;
}
final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
mHelper.isMobileDataEnabled());
final int defaultSubId = subscriptionManager.getDefaultDataSubscriptionId();
log("defaultSubId:" + defaultSubId);
if (!SubscriptionManager.isUsableSubscriptionId(defaultSubId)) {
return; // No subscription - do nothing.
}
MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
false /* disableOtherSubscriptions */);
}
@Override
public Intent getIntent() {
final String screenTitle = mContext.getText(R.string.provider_internet_settings).toString();
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
NetworkProviderSettings.class.getName(), "" /* key */, screenTitle,
SettingsEnums.SLICE)
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
.setData(getUri());
}
@Override
public Class getBackgroundWorkerClass() {
return NetworkProviderWorker.class;
}
@VisibleForTesting
ProviderModelSliceHelper getHelper() {
return new ProviderModelSliceHelper(mContext, this);
}
@VisibleForTesting
NetworkProviderWorker getWorker() {
return SliceBackgroundWorker.getInstance(getUri());
}
}

View File

@@ -0,0 +1,275 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.utils.ThreadUtils;
import com.android.wifitrackerlib.WifiEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* The helper is for slice of carrier and non-Carrier, used by ProviderModelSlice.
*/
public class ProviderModelSliceHelper {
private static final String TAG = "ProviderModelSlice";
private final SubscriptionManager mSubscriptionManager;
private final TelephonyManager mTelephonyManager;
protected final Context mContext;
private CustomSliceable mSliceable;
public ProviderModelSliceHelper(Context context, CustomSliceable sliceable) {
mContext = context;
mSliceable = sliceable;
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
}
private static void log(String s) {
Log.d(TAG, s);
}
protected ListBuilder.HeaderBuilder createHeader() {
return new ListBuilder.HeaderBuilder()
.setTitle(mContext.getText(R.string.summary_placeholder))
.setPrimaryAction(getPrimarySliceAction());
}
protected ListBuilder createListBuilder(Uri uri) {
final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY)
.setAccentColor(-1)
.setKeywords(getKeywords());
return builder;
}
protected GridRowBuilder createMessageGridRow(int messageResId) {
final CharSequence title = mContext.getText(messageResId);
return new GridRowBuilder()
// Add cells to the grid row.
.addCell(new GridRowBuilder.CellBuilder().addTitleText(title))
.setPrimaryAction(getPrimarySliceAction());
}
@Nullable
protected WifiSliceItem getConnectedWifiItem(List<WifiSliceItem> wifiList) {
if (wifiList == null) {
return null;
}
Optional<WifiSliceItem> item = wifiList.stream()
.filter(x -> x.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
.findFirst();
return item.isPresent() ? item.get() : null;
}
protected boolean hasCarrier() {
if (isAirplaneModeEnabled()
|| mSubscriptionManager == null || mTelephonyManager == null
|| mSubscriptionManager.getDefaultDataSubscriptionId()
== mSubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return false;
}
return true;
}
protected ListBuilder.RowBuilder createCarrierRow() {
final String title = getMobileTitle();
final String summary = getMobileSummary();
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
try {
drawable = getMobileDrawable(drawable);
} catch (Throwable e) {
e.printStackTrace();
}
final IconCompat levelIcon = Utils.createIconWithDrawable(drawable);
final PendingIntent toggleAction = mSliceable.getBroadcastIntent(mContext);
final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
"mobile_toggle" /* actionTitle */, isMobileDataEnabled());
final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
.setTitle(title)
.setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
.addEndItem(toggleSliceAction)
.setPrimaryAction(toggleSliceAction)
.setSubtitle(summary);
return rowBuilder;
}
protected SliceAction getPrimarySliceAction() {
return SliceAction.createDeeplink(
getPrimaryAction(),
Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)),
ListBuilder.ICON_IMAGE, mContext.getText(R.string.summary_placeholder));
}
private PendingIntent getPrimaryAction() {
final Intent intent = new Intent("android.settings.NETWORK_PROVIDER_SETTINGS")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0 /* requestCode */,
intent, PendingIntent.FLAG_IMMUTABLE /* flags */);
}
private boolean shouldInflateSignalStrength(int subId) {
return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
}
protected boolean isAirplaneModeEnabled() {
return WirelessUtils.isAirplaneModeOn(mContext);
}
protected boolean isMobileDataEnabled() {
if (mTelephonyManager == null) {
return false;
}
return mTelephonyManager.isDataEnabled();
}
protected boolean isDataSimActive() {
return MobileNetworkUtils.activeNetworkIsCellular(mContext);
}
protected boolean isNoCarrierData() {
if (mTelephonyManager == null) {
return false;
}
boolean mobileDataOnAndNoData = isMobileDataEnabled()
&& mTelephonyManager.getDataState() != mTelephonyManager.DATA_CONNECTED;
ServiceState serviceState = mTelephonyManager.getServiceState();
boolean mobileDataOffAndOutOfService = !isMobileDataEnabled() && serviceState != null
&& serviceState.getState() == serviceState.STATE_OUT_OF_SERVICE;
log("mobileDataOnAndNoData: " + mobileDataOnAndNoData
+ ",mobileDataOffAndOutOfService: " + mobileDataOffAndOutOfService);
return mobileDataOnAndNoData || mobileDataOffAndOutOfService;
}
private boolean isAirplaneSafeNetworksModeEnabled() {
// TODO: isAirplaneSafeNetworksModeEnabled is not READY
return false;
}
@VisibleForTesting
Drawable getMobileDrawable(Drawable drawable) throws Throwable {
// set color and drawable
if (mTelephonyManager == null) {
log("mTelephonyManager == null");
return drawable;
}
if (!isNoCarrierData()) {
Semaphore lock = new Semaphore(0);
AtomicReference<Drawable> shared = new AtomicReference<>();
ThreadUtils.postOnMainThread(() -> {
shared.set(getDrawableWithSignalStrength());
lock.release();
});
lock.acquire();
drawable = shared.get();
}
if (isDataSimActive()) {
drawable.setTint(Utils.getColorAccentDefaultColor(mContext));
}
return drawable;
}
/**
* To get the signal bar icon with level.
*
* @return The Drawable which is a signal bar icon with level.
*/
public Drawable getDrawableWithSignalStrength() {
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
if (mSubscriptionManager != null && shouldInflateSignalStrength(
mSubscriptionManager.getDefaultDataSubscriptionId())) {
level += 1;
numLevels += 1;
}
return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels,
NO_CELL_DATA_TYPE_ICON, false);
}
private String getMobileSummary() {
String summary = "";
//TODO: get radio technology.
String networkType = "";
if (isDataSimActive()) {
summary = mContext.getString(R.string.mobile_data_connection_active, networkType);
} else if (!isMobileDataEnabled()) {
summary = mContext.getString(R.string.mobile_data_off_summary);
}
return summary;
}
private String getMobileTitle() {
String title = mContext.getText(R.string.mobile_data_settings_title).toString();
if (mSubscriptionManager == null) {
return title;
}
final SubscriptionInfo defaultSubscription = mSubscriptionManager.getActiveSubscriptionInfo(
mSubscriptionManager.getDefaultDataSubscriptionId());
if (defaultSubscription != null) {
title = defaultSubscription.getDisplayName().toString();
}
return title;
}
protected SubscriptionManager getSubscriptionManager() {
return mSubscriptionManager;
}
private Set<String> getKeywords() {
final String keywords = mContext.getString(R.string.keywords_internet);
return Arrays.stream(TextUtils.split(keywords, ","))
.map(String::trim)
.collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.network.MobileDataContentObserver;
import com.android.settings.network.MobileDataEnabledListener;
import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.wifi.slice.WifiScanWorker;
import java.util.Collections;
import java.util.concurrent.Executor;
/**
* BackgroundWorker for Provider Model slice.
*/
public class NetworkProviderWorker extends WifiScanWorker implements
SignalStrengthListener.Callback, MobileDataEnabledListener.Client,
DataConnectivityListener.Client,
SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
private static final String TAG = "NetworkProviderWorker";
private static final int PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT = 4;
private DataContentObserver mMobileDataObserver;
private SignalStrengthListener mSignalStrengthListener;
private SubscriptionsChangeListener mSubscriptionsListener;
private MobileDataEnabledListener mDataEnabledListener;
private DataConnectivityListener mConnectivityListener;
private final Context mContext;
@VisibleForTesting
final PhoneStateListener mPhoneStateListener;
private final SubscriptionManager mSubscriptionManager;
private final TelephonyManager mTelephonyManager;
public NetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
// Mobile data worker
final Handler handler = new Handler(Looper.getMainLooper());
mMobileDataObserver = new DataContentObserver(handler, this);
mContext = context;
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
mPhoneStateListener = new NetworkProviderPhoneStateListener(handler::post);
mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
mDataEnabledListener = new MobileDataEnabledListener(context, this);
mConnectivityListener = new DataConnectivityListener(context, this);
mSignalStrengthListener = new SignalStrengthListener(context, this);
}
@Override
protected void onSlicePinned() {
mMobileDataObserver.register(mContext,
getDefaultSubscriptionId(mSubscriptionManager));
mSubscriptionsListener.start();
mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
mConnectivityListener.start();
mSignalStrengthListener.resume();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE
| PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
super.onSlicePinned();
}
@Override
protected void onSliceUnpinned() {
mMobileDataObserver.unregister(mContext);
mSubscriptionsListener.stop();
mDataEnabledListener.stop();
mConnectivityListener.stop();
mSignalStrengthListener.pause();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
super.onSliceUnpinned();
}
@Override
public void close() {
mMobileDataObserver = null;
super.close();
}
@Override
public int getApRowCount() {
return PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT;
}
/**
* To update the Slice.
*/
public void updateSlice() {
notifySliceChange();
}
@Override
public void onSubscriptionsChanged() {
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
Log.d(TAG, "onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId);
mSignalStrengthListener.updateSubscriptionIds(
SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)
? Collections.singleton(defaultDataSubId) : Collections.emptySet());
if (defaultDataSubId != mDataEnabledListener.getSubId()) {
mDataEnabledListener.stop();
mDataEnabledListener.start(defaultDataSubId);
}
updateSlice();
}
@Override
public void onSignalStrengthChanged() {
Log.d(TAG, "onSignalStrengthChanged");
updateSlice();
}
@Override
public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
Log.d(TAG, "onAirplaneModeChanged");
updateSlice();
}
@Override
public void onMobileDataEnabledChange() {
Log.d(TAG, "onMobileDataEnabledChange");
updateSlice();
}
@Override
public void onDataConnectivityChange() {
Log.d(TAG, "onDataConnectivityChange");
updateSlice();
}
/**
* Listen to update of mobile data change.
*/
public class DataContentObserver extends ContentObserver {
private final NetworkProviderWorker mNetworkProviderWorker;
public DataContentObserver(Handler handler, NetworkProviderWorker backgroundWorker) {
super(handler);
mNetworkProviderWorker = backgroundWorker;
}
@Override
public void onChange(boolean selfChange) {
mNetworkProviderWorker.updateSlice();
}
/**
* To register the observer for mobile data changed.
* @param context the Context object.
* @param subId the default data subscription id.
*/
public void register(Context context, int subId) {
final Uri uri = MobileDataContentObserver.getObservableUri(context, subId);
context.getContentResolver().registerContentObserver(uri, false, this);
}
/**
* To unregister the observer for mobile data changed.
* @param context the Context object.
*/
public void unregister(Context context) {
context.getContentResolver().unregisterContentObserver(this);
}
}
class NetworkProviderPhoneStateListener extends PhoneStateListener {
NetworkProviderPhoneStateListener(Executor executor) {
super(executor);
}
@Override
public void onServiceStateChanged(ServiceState state) {
Log.d(TAG, "onServiceStateChanged voiceState=" + state.getState()
+ " dataState=" + state.getDataRegistrationState());
updateSlice();
}
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
Log.d(TAG, "onActiveDataSubscriptionIdChanged: subId=" + subId);
updateSlice();
}
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
Log.d(TAG, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
updateSlice();
}
}
protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) {
final SubscriptionInfo defaultSubscription = subscriptionManager.getActiveSubscriptionInfo(
subscriptionManager.getDefaultDataSubscriptionId());
if (defaultSubscription == null) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription
}
return defaultSubscription.getSubscriptionId();
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.panel;
import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
@@ -51,17 +53,19 @@ public class InternetConnectivityPanel implements PanelContent {
@Override
public CharSequence getTitle() {
return mContext.getText(R.string.internet_connectivity_panel_title);
return mContext.getText(Utils.isProviderModelEnabled(mContext)
? R.string.provider_internet_settings : R.string.internet_connectivity_panel_title);
}
@Override
public List<Uri> getSlices() {
final List<Uri> uris = new ArrayList<>();
uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
if (Utils.isProviderModelEnabled(mContext)) {
uris.add(CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI);
uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI);
} else {
uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
uris.add(AirplaneModePreferenceController.SLICE_URI);
}
return uris;
@@ -69,7 +73,8 @@ public class InternetConnectivityPanel implements PanelContent {
@Override
public Intent getSeeMoreIntent() {
return new Intent(Settings.ACTION_WIRELESS_SETTINGS)
return new Intent(Utils.isProviderModelEnabled(mContext)
? ACTION_NETWORK_PROVIDER_SETTINGS : Settings.ACTION_WIRELESS_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

View File

@@ -40,6 +40,7 @@ import com.android.settings.location.LocationSlice;
import com.android.settings.media.MediaOutputIndicatorSlice;
import com.android.settings.media.RemoteMediaSlice;
import com.android.settings.network.AirplaneSafeNetworksSlice;
import com.android.settings.network.ProviderModelSlice;
import com.android.settings.network.telephony.MobileDataSlice;
import com.android.settings.notification.zen.ZenModeButtonPreferenceController;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -167,6 +168,17 @@ public class CustomSliceRegistry {
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("mobile_data")
.build();
/**
* Full {@link Uri} for the Provider Model Slice.
*/
public static final Uri PROVIDER_MODEL_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("provider_model")
.build();
/**
* Full {@link Uri} for the Alarm volume Slice.
*/
@@ -176,6 +188,7 @@ public class CustomSliceRegistry {
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("alarm_volume")
.build();
/**
* Full {@link Uri} for the Call Volume Slice.
*/
@@ -319,6 +332,7 @@ public class CustomSliceRegistry {
sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
sUriToSlice.put(PROVIDER_MODEL_SLICE_URI, ProviderModelSlice.class);
sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);

View File

@@ -61,7 +61,7 @@ public class WifiScanWorker extends SliceBackgroundWorker<WifiSliceItem> impleme
@VisibleForTesting
final LifecycleRegistry mLifecycleRegistry;
@VisibleForTesting
WifiPickerTracker mWifiPickerTracker;
protected WifiPickerTracker mWifiPickerTracker;
// Worker thread used for WifiPickerTracker work
private final HandlerThread mWorkerThread;

View File

@@ -147,7 +147,7 @@ public class WifiSlice implements CustomSliceable {
return builder;
}
private ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
protected ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
final CharSequence title = wifiSliceItem.getTitle();
final IconCompat levelIcon = getWifiSliceItemLevelIcon(wifiSliceItem);
final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()

View File

@@ -0,0 +1,307 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.slice.Slice;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.GridRowBuilder.CellBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Utils;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class ProviderModelSliceHelperTest {
private Context mContext;
private MockProviderModelSliceHelper mProviderModelSliceHelper;
private PersistableBundle mBundle;
private Network mNetwork;
private NetworkCapabilities mNetworkCapabilities;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private CarrierConfigManager mCarrierConfigManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private ServiceState mServiceState;
@Mock
private WifiSliceItem mWifiSliceItem1;
@Mock
private WifiSliceItem mWifiSliceItem2;
@Mock
private SubscriptionInfo mDefaultDataSubscriptionInfo;
@Mock
private Drawable mDrawableWithSignalStrength;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mBundle = new PersistableBundle();
mNetwork = new Network(anyInt());
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
mCarrierConfigManager);
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
TestCustomSliceable testCustomSliceable = new TestCustomSliceable();
mProviderModelSliceHelper = new MockProviderModelSliceHelper(mContext, testCustomSliceable);
}
@Test
public void createMessageGridRow_inputTheResourceId_verifyTitle() {
int messageResId = ResourcesUtils.getResourcesId(mContext, "string",
"non_carrier_network_unavailable");
CharSequence title = ResourcesUtils.getResourcesString(mContext,
"non_carrier_network_unavailable");
GridRowBuilder testGridRow = mProviderModelSliceHelper.createMessageGridRow(messageResId);
List<CellBuilder> cellItem = testGridRow.getCells();
assertThat(cellItem.get(0).getTitle()).isEqualTo(title);
}
@Test
public void getConnectedWifiItem_inputListInvolveOneConnectedWifiItem_verifyReturnItem() {
when(mWifiSliceItem1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mWifiSliceItem2.getConnectedState()).thenReturn(
WifiEntry.CONNECTED_STATE_DISCONNECTED);
List<WifiSliceItem> wifiList = new ArrayList<>();
wifiList.add(mWifiSliceItem1);
wifiList.add(mWifiSliceItem2);
WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
assertThat(testItem).isNotNull();
assertEquals(mWifiSliceItem1, testItem);
}
@Test
public void getConnectedWifiItem_inputListInvolveNoConnectedWifiItem_verifyReturnItem() {
when(mWifiSliceItem1.getConnectedState()).thenReturn(
WifiEntry.CONNECTED_STATE_DISCONNECTED);
when(mWifiSliceItem2.getConnectedState()).thenReturn(
WifiEntry.CONNECTED_STATE_DISCONNECTED);
List<WifiSliceItem> wifiList = new ArrayList<>();
wifiList.add(mWifiSliceItem1);
wifiList.add(mWifiSliceItem2);
WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
assertThat(testItem).isNull();
}
@Test
public void getConnectedWifiItem_inputNull_verifyReturnItem() {
List<WifiSliceItem> wifiList = null;
WifiSliceItem testItem = mProviderModelSliceHelper.getConnectedWifiItem(wifiList);
assertThat(testItem).isNull();
}
@Test
public void createCarrierRow_hasDefaultDataSubscriptionId_verifyTitle() {
String expectDisplayName = "Name1";
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
mDefaultDataSubscriptionInfo);
when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow();
assertThat(testRowBuild.getTitle()).isEqualTo(expectDisplayName);
}
@Test
public void isNoCarrierData_mobileDataOnAndNoData_returnTrue() {
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
}
@Test
public void isNoCarrierData_mobileDataOffAndOutOfService_returnTrue() {
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
}
@Test
public void isNoCarrierData_mobileDataOnAndDataConnected_returnFalse() {
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
}
@Test
public void isNoCarrierData_mobileDataOffAndVoiceIsInService_returnFalse() {
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
}
@Test
public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable {
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
mDefaultDataSubscriptionInfo);
when(mConnectivityManager.getActiveNetwork()).thenReturn(null);
Drawable expectDrawable = mock(Drawable.class);
assertThat(mProviderModelSliceHelper.getMobileDrawable(expectDrawable)).isEqualTo(
expectDrawable);
}
@Test
public void getMobileDrawable_hasCarrierDataAndDataIsOnCellular_getMobileDrawable()
throws Throwable {
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
Drawable drawable = mock(Drawable.class);
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
mDefaultDataSubscriptionInfo);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
mDrawableWithSignalStrength);
verify(mDrawableWithSignalStrength).setTint(Utils.getColorAccentDefaultColor(mContext));
}
@Test
public void getMobileDrawable_hasCarrierDataAndDataIsOnWifi_getMobileDrawable()
throws Throwable {
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
Drawable drawable = mock(Drawable.class);
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
mDefaultDataSubscriptionInfo);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
mDrawableWithSignalStrength);
}
private void addNetworkTransportType(int networkType) {
mNetworkCapabilities = new NetworkCapabilities().addTransportType(networkType);
when(mConnectivityManager.getNetworkCapabilities(mNetwork)).thenReturn(
mNetworkCapabilities);
}
private class TestCustomSliceable implements CustomSliceable {
TestCustomSliceable() {
}
@Override
public Slice getSlice() {
return null;
}
@Override
public Uri getUri() {
return Uri.parse("content://android.settings.slices/action/provider_model");
}
@Override
public Intent getIntent() {
return new Intent();
}
}
private class MockProviderModelSliceHelper extends ProviderModelSliceHelper {
MockProviderModelSliceHelper(Context context, CustomSliceable sliceable) {
super(context, sliceable);
}
@Override
public Drawable getDrawableWithSignalStrength() {
return mDrawableWithSignalStrength;
}
}
}

View File

@@ -0,0 +1,333 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.widget.SliceLiveData;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Utils;
import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class ProviderModelSliceTest {
private static final Uri PROVIDER_MODEL_SLICE_URI =
Uri.parse("content://com.android.settings.slices/action/provider_model");
private static final int MOCK_SLICE_LEVEL = 3;
private Context mContext;
private MockProviderModelSlice mMockProviderModelSlice;
List<WifiSliceItem> mWifiList = new ArrayList<>();
private ListBuilder mListBuilder;
private MockNetworkProviderWorker mMockNetworkProviderWorker;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private WifiManager mWifiManager;
@Mock
private ProviderModelSliceHelper mProviderModelSliceHelper;
@Mock
private WifiSliceItem mMockWifiSliceItem1;
@Mock
private WifiSliceItem mMockWifiSliceItem2;
@Mock
private WifiSliceItem mMockWifiSliceItem3;
@Mock
ListBuilder.RowBuilder mMockCarrierRowBuild;
@Mock
ListBuilder.HeaderBuilder mMockHeader;
@Mock
GridRowBuilder mMockGridRowBuilderNonCarrierNetworkUnavailable;
@Mock
GridRowBuilder mMockGridRowBuilderAllNetworkUnavailable;
@Before
@UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
PROVIDER_MODEL_SLICE_URI);
mMockProviderModelSlice = new MockProviderModelSlice(mContext, mMockNetworkProviderWorker);
mListBuilder = spy(new ListBuilder(mContext, PROVIDER_MODEL_SLICE_URI,
ListBuilder.INFINITY).setAccentColor(-1));
when(mProviderModelSliceHelper.createListBuilder(PROVIDER_MODEL_SLICE_URI)).thenReturn(
mListBuilder);
mWifiList = new ArrayList<>();
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockBuilder();
}
@Test
@UiThreadTest
public void getSlice_noWorkerAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).setHeader(mMockHeader);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockNetworkProviderWorker.updateSelfResults(null);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).setHeader(mMockHeader);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndHasCarrierNoData_oneCarrierOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockNetworkProviderWorker.updateSelfResults(null);
mockHelperCondition(false, true, true, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndNoCarrier_oneCarrierOneGridRowWithNonCarrierNetworkUnavailable() {
mWifiList.clear();
mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
mockHelperCondition(false, true, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderNonCarrierNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_haveTwoWifiAndOneCarrier_getCarrierAndTwoWiFi() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_CONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, true, false, mWifiList.get(0));
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
@UiThreadTest
public void getSlice_haveOneConnectedWifiAndTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_CONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mockWifiItemCondition(mMockWifiSliceItem3, "wifi3", "wifi3",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi3_key", true);
mWifiList.add(mMockWifiSliceItem3);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, false, false, mWifiList.get(0));
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
@UiThreadTest
public void getSlice_haveTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(2)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
public void providerModelSlice_hasCorrectUri() {
assertThat(mMockProviderModelSlice.getUri()).isEqualTo(PROVIDER_MODEL_SLICE_URI);
}
private void mockHelperCondition(boolean airplaneMode, boolean hasCarrier,
boolean isNoCarrierData, WifiSliceItem connectedWifiItem) {
when(mProviderModelSliceHelper.isAirplaneModeEnabled()).thenReturn(airplaneMode);
when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier);
when(mProviderModelSliceHelper.isNoCarrierData()).thenReturn(isNoCarrierData);
when(mProviderModelSliceHelper.getConnectedWifiItem(any())).thenReturn(connectedWifiItem);
}
private void mockWifiItemCondition(WifiSliceItem mockWifiItem, String title, String summary,
int connectedState, String key, boolean shouldEditBeforeConnect) {
when(mockWifiItem.getTitle()).thenReturn(title);
when(mockWifiItem.getSummary()).thenReturn(summary);
when(mockWifiItem.getConnectedState()).thenReturn(connectedState);
when(mockWifiItem.getLevel()).thenReturn(MOCK_SLICE_LEVEL);
when(mockWifiItem.getKey()).thenReturn(key);
when(mockWifiItem.shouldEditBeforeConnect()).thenReturn(shouldEditBeforeConnect);
}
private void mockBuilder() {
SliceAction mockSliceAction = getPrimarySliceAction();
when(mMockHeader.getTitle()).thenReturn("mockHeader");
when(mMockHeader.getPrimaryAction()).thenReturn(mockSliceAction);
when(mProviderModelSliceHelper.createHeader()).thenReturn(mMockHeader);
int resId = ResourcesUtils.getResourcesId(mContext, "string",
"non_carrier_network_unavailable");
when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
mMockGridRowBuilderNonCarrierNetworkUnavailable);
resId = ResourcesUtils.getResourcesId(mContext, "string",
"all_network_unavailable");
when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
mMockGridRowBuilderAllNetworkUnavailable);
when(mMockCarrierRowBuild.getTitle()).thenReturn("mockRow");
when(mMockCarrierRowBuild.getPrimaryAction()).thenReturn(mockSliceAction);
when(mProviderModelSliceHelper.createCarrierRow()).thenReturn(mMockCarrierRowBuild);
}
private SliceAction getPrimarySliceAction() {
return SliceAction.createDeeplink(
getPrimaryAction(),
Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)),
ListBuilder.ICON_IMAGE,
ResourcesUtils.getResourcesString(mContext, "summary_placeholder"));
}
private PendingIntent getPrimaryAction() {
final Intent intent = new Intent("android.settings.NETWORK_PROVIDER_SETTINGS")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0 /* requestCode */,
intent, PendingIntent.FLAG_IMMUTABLE /* flags */);
}
public class MockNetworkProviderWorker extends NetworkProviderWorker {
MockNetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
}
public void updateSelfResults(List<WifiSliceItem> results) {
this.updateResults(results);
}
}
public class MockProviderModelSlice extends ProviderModelSlice {
private MockNetworkProviderWorker mNetworkProviderWorker;
MockProviderModelSlice(Context context, MockNetworkProviderWorker networkProviderWorker) {
super(context);
mNetworkProviderWorker = networkProviderWorker;
}
@Override
ProviderModelSliceHelper getHelper() {
return mProviderModelSliceHelper;
}
@Override
NetworkProviderWorker getWorker() {
return mNetworkProviderWorker;
}
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony;
import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import androidx.lifecycle.Lifecycle;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class NetworkProviderWorkerTest {
private Context mContext;
private MockNetworkProviderWorker mMockNetworkProviderWorker;
@Mock
WifiPickerTracker mMockWifiPickerTracker;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Before
@UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
PROVIDER_MODEL_SLICE_URI);
mMockNetworkProviderWorker.setWifiPickerTracker(mMockWifiPickerTracker);
}
@Test
@UiThreadTest
public void onConstructor_shouldBeInCreatedState() {
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.CREATED);
}
@Test
@UiThreadTest
public void onSlicePinned_shouldBeInResumedState() {
mMockNetworkProviderWorker.onSlicePinned();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.RESUMED);
}
@Test
@UiThreadTest
public void onSliceUnpinned_shouldBeInCreatedState() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.onSliceUnpinned();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.CREATED);
}
@Test
@UiThreadTest
public void close_shouldBeInDestroyedState() {
mMockNetworkProviderWorker.close();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.DESTROYED);
}
@Test
@UiThreadTest
public void getWifiEntry_connectedWifiKey_shouldGetConnectedWifi() {
final String key = "key";
final WifiEntry connectedWifiEntry = mock(WifiEntry.class);
when(connectedWifiEntry.getKey()).thenReturn(key);
when(mMockWifiPickerTracker.getConnectedWifiEntry()).thenReturn(connectedWifiEntry);
assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(connectedWifiEntry);
}
@Test
@UiThreadTest
public void getWifiEntry_reachableWifiKey_shouldGetReachableWifi() {
final String key = "key";
final WifiEntry reachableWifiEntry = mock(WifiEntry.class);
when(reachableWifiEntry.getKey()).thenReturn(key);
when(mMockWifiPickerTracker.getWifiEntries()).thenReturn(Arrays.asList(reachableWifiEntry));
assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(reachableWifiEntry);
}
@Test
@UiThreadTest
public void onSubscriptionsChanged_notifySubscriptionChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onSubscriptionsChanged();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onAirplaneModeChanged_airplaneModeOn_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onAirplaneModeChanged(false);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onAirplaneModeChanged_airplaneModeOff_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onAirplaneModeChanged(true);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onSignalStrengthChanged_notifySignalStrengthChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onSignalStrengthChanged();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onMobileDataEnabledChange_notifyMobileDataEnabledChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onMobileDataEnabledChange();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onDataConnectivityChange_notifyDataConnectivityChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onDataConnectivityChange();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onServiceStateChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onServiceStateChanged(new ServiceState());
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onActiveDataSubscriptionIdChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onActiveDataSubscriptionIdChanged(1);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onDisplayInfoChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onDisplayInfoChanged(
new TelephonyDisplayInfo(14, 0));
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
public class MockNetworkProviderWorker extends NetworkProviderWorker {
private boolean mHasNotification = false;
MockNetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
}
public void receiveNotification(boolean inputValue) {
mHasNotification = inputValue;
}
public boolean hasNotification() {
return mHasNotification;
}
@Override
public void updateSlice() {
super.updateSlice();
receiveNotification(true);
}
public void setWifiPickerTracker(WifiPickerTracker wifiPickerTracker) {
mWifiPickerTracker = wifiPickerTracker;
}
}
}

View File

@@ -17,15 +17,43 @@ package com.android.settings.testutils;
import android.content.Context;
/**
* Test util to provide the correct resources.
*/
public final class ResourcesUtils {
/**
* Return a resource identifier for the given resource name.
* @param context Context to use.
* @param type Optional default resource type to find, if "type/" is not included in the name.
* Can be null to require an explicit type.
* @param name The name of the desired resource.
* @return The associated resource identifier. Returns 0 if no such resource was found.
* (0 is not a valid resource ID.)
*/
public static int getResourcesId(Context context, String type, String name) {
return context.getResources().getIdentifier(name, type, context.getPackageName());
}
/**
* Returns a localized string from the application's package's default string table.
* @param context Context to use.
* @param name The name of the desired resource.
* @return The string data associated with the resource, stripped of styled text information.
*/
public static String getResourcesString(Context context, String name) {
return context.getResources().getString(getResourcesId(context, "string", name));
}
/**
* Return the string value associated with a particular neame of resource,
* substituting the format arguments as defined in {@link java.util.Formatter}
* and {@link java.lang.String#format}. It will be stripped of any styled text
* information.
* @param context Context to use.
* @param name The name of the desired resource.
* @param value The format arguments that will be used for substitution.
* @return The string data associated with the resource, stripped of styled text information.
*/
public static String getResourcesString(Context context, String name, Object... value) {
return context.getResources().getString(getResourcesId(context, "string", name), value);
}