[ProviderModel] Add InternetDialogController for internet dialog

- Use OnSubscriptionsChangedListener for mobile data
    - Ues WifiPickerTrackerFactory for wifi list
    - Add InternetDialogControllerTest for unit test

Bug: 187779230
Test: atest InternetDialogControllerTest
Change-Id: I37a423f31ae80963147758160ac0234ae17469f9
Merged-In: I37a423f31ae80963147758160ac0234ae17469f9
This commit is contained in:
Zoey Chen
2021-08-13 12:44:50 +08:00
parent 35062e8426
commit 7b5e298218
8 changed files with 1216 additions and 0 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

@@ -1590,4 +1590,7 @@
<!-- The padding between the icon and the text. -->
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
<!-- Signal icon in internet dialog -->
<dimen name="signal_strength_icon_size">24dp</dimen>
</resources>

View File

@@ -2994,4 +2994,37 @@
<string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
<!-- URL for more information about changes in global actions -->
<string name="global_actions_change_url" translatable="false"></string>
<!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
<string name="airplane_mode">Airplane mode</string>
<!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
<string name="mobile_data_settings_title">Mobile data</string>
<!-- Provider Model: Summary text separator for preferences including a short description
(eg. "Connected / 5G"). [CHAR LIMIT=50] -->
<string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
<!-- Provider Model:
Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_connection_active">Connected</string>
<!-- Provider Model:
Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
<!-- Provider Model:
Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
<string name="mobile_data_no_connection">No connection</string>
<!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
<string name="non_carrier_network_unavailable">No other networks available</string>
<!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
<string name="all_network_unavailable">No networks available</string>
<!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
<string name="turn_on_wifi">Wi\u2011Fi</string>
<!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
<string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
<!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
<string name="tap_a_network_to_connect">Tap a network to connect</string>
<!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
<string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
<!-- Provider Model: Failure notification for connect -->
<string name="wifi_failed_connect_message">Failed to connect to network</string>
<!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
<string name="see_all_networks">See all</string>
</resources>

View File

@@ -0,0 +1,851 @@
/*
* Copyright (C) 2021 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.systemui.qs.tiles.dialog;
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
import android.annotation.ColorInt;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.DeviceInfoUtils;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
public class InternetDialogController implements WifiEntry.DisconnectCallback,
NetworkController.AccessPointController.AccessPointCallback {
private static final String TAG = "InternetDialogController";
private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
"android.settings.NETWORK_PROVIDER_SETTINGS";
private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
public static final int NO_CELL_DATA_TYPE_ICON = 0;
private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
R.string.tap_a_network_to_connect;
private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
R.string.wifi_empty_list_wifi_on;
private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
R.string.non_carrier_network_unavailable;
private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
R.string.all_network_unavailable;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private WifiManager mWifiManager;
private Context mContext;
private ActivityStarter mActivityStarter;
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
private ConnectivityManager mConnectivityManager;
private TelephonyDisplayInfo mTelephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
private Handler mHandler;
private MobileMappings.Config mConfig = null;
private Executor mExecutor;
private AccessPointController mAccessPointController;
private IntentFilter mWifiStateFilter;
private InternetDialogCallback mCallback;
private List<WifiEntry> mWifiEntry;
private WifiEntry mConnectedEntry;
private UiEventLogger mUiEventLogger;
private BroadcastDispatcher mBroadcastDispatcher;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@VisibleForTesting
protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@VisibleForTesting
protected InternetTelephonyCallback mInternetTelephonyCallback;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshCarrierInfo() {
mCallback.onRefreshCarrierInfo();
}
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
mCallback.onSimStateChanged();
}
};
protected List<SubscriptionInfo> getSubscriptionInfo() {
return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
}
@Inject
public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
ActivityStarter starter, AccessPointController accessPointController,
SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
GlobalSettings globalSettings) {
if (DEBUG) {
Log.d(TAG, "Init InternetDialogController");
}
mHandler = handler;
mExecutor = mainExecutor;
mContext = context;
mGlobalSettings = globalSettings;
mWifiManager = wifiManager;
mTelephonyManager = telephonyManager;
mConnectivityManager = connectivityManager;
mSubscriptionManager = subscriptionManager;
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mUiEventLogger = uiEventLogger;
mActivityStarter = starter;
mAccessPointController = accessPointController;
mConfig = MobileMappings.Config.readConfig(mContext);
}
void onStart(@NonNull InternetDialogCallback callback) {
if (DEBUG) {
Log.d(TAG, "onStart");
}
mCallback = callback;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
mAccessPointController.addAccessPointCallback(this);
mBroadcastDispatcher.registerReceiver(mWifiStateReceiver, mWifiStateFilter, mExecutor);
// Listen the subscription changes
mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mOnSubscriptionsChangedListener);
mDefaultDataSubId = getDefaultDataSubscriptionId();
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mInternetTelephonyCallback = new InternetTelephonyCallback();
mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
// Listen the connectivity changes
mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), new DataConnectivityListener(), mHandler);
scanWifiAccessPoints();
}
void onStop() {
if (DEBUG) {
Log.d(TAG, "onStop");
}
mBroadcastDispatcher.unregisterReceiver(mWifiStateReceiver);
mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
mSubscriptionManager.removeOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
}
@VisibleForTesting
boolean isAirplaneModeEnabled() {
return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
@VisibleForTesting
protected int getDefaultDataSubscriptionId() {
return mSubscriptionManager.getDefaultDataSubscriptionId();
}
@VisibleForTesting
protected Intent getSettingsIntent() {
return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
CharSequence getDialogTitleText() {
if (isAirplaneModeEnabled()) {
return mContext.getText(R.string.airplane_mode);
}
return mContext.getText(R.string.quick_settings_internet_label);
}
CharSequence getSubtitleText(boolean isProgressBarVisible) {
if (isAirplaneModeEnabled()) {
return null;
}
if (!mWifiManager.isWifiEnabled()) {
// When the airplane mode is off and Wi-Fi is disabled.
// Sub-Title: Wi-Fi is off
if (DEBUG) {
Log.d(TAG, "Airplane mode off + Wi-Fi off.");
}
return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
}
if (isProgressBarVisible) {
// When the Wi-Fi scan result callback is received
// Sub-Title: Searching for networks...
return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
}
final List<ScanResult> wifiList = mWifiManager.getScanResults();
if (wifiList != null && wifiList.size() != 0) {
return mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT);
}
// Sub-Title:
// show non_carrier_network_unavailable
// - while Wi-Fi on + no Wi-Fi item
// - while Wi-Fi on + no Wi-Fi item + mobile data off
// show all_network_unavailable:
// - while Wi-Fi on + no Wi-Fi item + no carrier item
// - while Wi-Fi on + no Wi-Fi item + service is out of service
// - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
if (DEBUG) {
Log.d(TAG, "No Wi-Fi item.");
}
if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
if (DEBUG) {
Log.d(TAG, "No carrier or service is out of service.");
}
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
if (!isMobileDataEnabled()) {
if (DEBUG) {
Log.d(TAG, "Mobile data off");
}
return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
}
if (!activeNetworkIsCellular()) {
if (DEBUG) {
Log.d(TAG, "No carrier data.");
}
return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
}
return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
}
Drawable getWifiConnectedDrawable(WifiEntry wifiEntry) throws Throwable {
final @ColorInt int tint;
tint = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.colorAccentPrimaryVariant);
final Drawable drawable = mContext.getDrawable(
com.android.settingslib.Utils.getWifiIconResource(wifiEntry.getLevel()));
drawable.setTint(tint);
return drawable;
}
Drawable getSignalStrengthDrawable() {
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
try {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null");
}
return drawable;
}
if (isDataStateInService() || isVoiceStateInService()) {
AtomicReference<Drawable> shared = new AtomicReference<>();
shared.set(getSignalStrengthDrawableWithLevel());
drawable = shared.get();
}
drawable.setTint(
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal));
if (activeNetworkIsCellular()) {
drawable.setTint(Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.colorAccentPrimaryVariant));
}
} catch (Throwable e) {
e.printStackTrace();
}
return drawable;
}
/**
* To get the signal bar icon with level.
*
* @return The Drawable which is a signal bar icon with level.
*/
Drawable getSignalStrengthDrawableWithLevel() {
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
level += 1;
numLevels += 1;
}
return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
}
Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
int iconType, boolean cutOut) {
Log.d(TAG, "getSignalStrengthIcon");
final SignalDrawable signalDrawable = new SignalDrawable(context);
signalDrawable.setLevel(
SignalDrawable.getState(level, numLevels, cutOut));
// Make the network type drawable
final Drawable networkDrawable =
iconType == NO_CELL_DATA_TYPE_ICON
? EMPTY_DRAWABLE
: context.getResources().getDrawable(iconType, context.getTheme());
// Overlay the two drawables
final Drawable[] layers = {networkDrawable, signalDrawable};
final int iconSize =
context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
final LayerDrawable icons = new LayerDrawable(layers);
// Set the network type icon at the top left
icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
// Set the signal strength icon at the bottom right
icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
return icons;
}
private boolean shouldInflateSignalStrength(int subId) {
return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
}
private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
return displayNames.getOrDefault(subscriptionId, "");
}
private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
}
// Map of SubscriptionId to DisplayName
final Supplier<Stream<DisplayInfo>> originalInfos =
() -> getSubscriptionInfo()
.stream()
.filter(i -> {
// Filter out null values.
return (i != null && i.getDisplayName() != null);
})
.map(i -> {
DisplayInfo info = new DisplayInfo();
info.subscriptionInfo = i;
info.originalName = i.getDisplayName().toString().trim();
return info;
});
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
// Return the set of duplicate names
final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
.filter(info -> !uniqueNames.add(info.originalName))
.map(info -> info.originalName)
.collect(Collectors.toSet());
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
if (duplicateOriginalNames.contains(info.originalName)) {
// This may return null, if the user cannot view the phone number itself.
final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
info.subscriptionInfo);
String lastFourDigits = "";
if (phoneNumber != null) {
lastFourDigits = (phoneNumber.length() > 4)
? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
}
if (TextUtils.isEmpty(lastFourDigits)) {
info.uniqueName = info.originalName;
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
}
} else {
info.uniqueName = info.originalName;
}
return info;
});
// Check uniqueness a second time.
// We might not have had permission to view the phone numbers.
// There might also be multiple phone numbers whose last 4 digits the same.
uniqueNames.clear();
final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
.filter(info -> !uniqueNames.add(info.uniqueName))
.map(info -> info.uniqueName)
.collect(Collectors.toSet());
return uniqueInfos.get().map(info -> {
if (duplicatePhoneNames.contains(info.uniqueName)) {
info.uniqueName = info.originalName + " "
+ info.subscriptionInfo.getSubscriptionId();
}
return info;
}).collect(Collectors.toMap(
info -> info.subscriptionInfo.getSubscriptionId(),
info -> info.uniqueName));
}
CharSequence getMobileNetworkTitle() {
return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
}
String getMobileNetworkSummary() {
String description = getNetworkTypeDescription(mContext, mConfig,
mTelephonyDisplayInfo, mDefaultDataSubId);
return getMobileSummary(mContext, mTelephonyManager, description);
}
/**
* Get currently description of mobile network type.
*/
private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
String iconKey = getIconKey(telephonyDisplayInfo);
if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
if (DEBUG) {
Log.d(TAG, "The description of network type is empty.");
}
return "";
}
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
return resId != 0
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
private String getMobileSummary(Context context, TelephonyManager telephonyManager,
String networkTypeDescription) {
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
if (!isDataStateInService()) {
return context.getString(R.string.mobile_data_no_connection);
}
String summary = networkTypeDescription;
if (activeNetworkIsCellular()) {
summary = context.getString(R.string.preference_summary_default_combination,
context.getString(R.string.mobile_data_connection_active),
networkTypeDescription);
}
return summary;
}
String getConnectedWifiTitle() {
if (getConnectedWifiEntry() == null) {
if (DEBUG) {
Log.d(TAG, "connected entry is null");
}
return "";
}
return getConnectedWifiEntry().getTitle();
}
String getConnectedWifiSummary() {
if (getConnectedWifiEntry() == null) {
if (DEBUG) {
Log.d(TAG, "connected entry is null");
}
return "";
}
return getConnectedWifiEntry().getSummary(false);
}
void launchNetworkSetting() {
mCallback.dismissDialog();
mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
}
void connectCarrierNetwork() {
final MergedCarrierEntry mergedCarrierEntry =
mAccessPointController.getMergedCarrierEntry();
if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
mergedCarrierEntry.connect(null /* ConnectCallback */);
}
}
List<WifiEntry> getWifiEntryList() {
return mWifiEntry;
}
WifiEntry getConnectedWifiEntry() {
return mConnectedEntry;
}
WifiManager getWifiManager() {
return mWifiManager;
}
TelephonyManager getTelephonyManager() {
return mTelephonyManager;
}
SubscriptionManager getSubscriptionManager() {
return mSubscriptionManager;
}
/**
* @return whether there is the carrier item in the slice.
*/
boolean hasCarrier() {
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
}
return false;
}
if (isAirplaneModeEnabled() || mTelephonyManager == null
|| mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
return false;
}
return true;
}
/**
* Return {@code true} if mobile data is enabled
*/
boolean isMobileDataEnabled() {
if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
return false;
}
return true;
}
/**
* Set whether to enable data for {@code subId}, also whether to disable data for other
* subscription
*/
void setMobileDataEnabled(Context context, int subId, boolean enabled,
boolean disableOtherSubscriptions) {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
}
return;
}
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
}
return;
}
mTelephonyManager.setDataEnabled(enabled);
if (disableOtherSubscriptions) {
final List<SubscriptionInfo> subInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
if (subInfoList != null) {
for (SubscriptionInfo subInfo : subInfoList) {
// We never disable mobile data for opportunistic subscriptions.
if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
context.getSystemService(TelephonyManager.class).createForSubscriptionId(
subInfo.getSubscriptionId()).setDataEnabled(false);
}
}
}
}
}
boolean isDataStateInService() {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not detect mobile state.");
}
return false;
}
return mTelephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED;
}
boolean isVoiceStateInService() {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
}
return false;
}
final ServiceState serviceState = mTelephonyManager.getServiceState();
return serviceState != null
&& serviceState.getState() == serviceState.STATE_IN_SERVICE;
}
boolean activeNetworkIsCellular() {
if (mConnectivityManager == null) {
if (DEBUG) {
Log.d(TAG, "ConnectivityManager is null, can not check active network.");
}
return false;
}
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
return false;
}
final NetworkCapabilities networkCapabilities =
mConnectivityManager.getNetworkCapabilities(activeNetwork);
if (networkCapabilities == null) {
return false;
}
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
}
boolean connect(WifiEntry ap) {
if (ap == null) {
if (DEBUG) {
Log.d(TAG, "No Wi-Fi ap to connect.");
}
return false;
}
if (ap.getWifiConfiguration() != null) {
if (DEBUG) {
Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
}
} else {
if (DEBUG) {
Log.d(TAG, "connect to unsaved network " + ap.getTitle());
}
}
ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
return false;
}
static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
final ActivityStarter mActivityStarter;
final Context mContext;
final WifiEntry mWifiEntry;
WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
WifiEntry connectWifiEntry) {
mActivityStarter = activityStarter;
mContext = context;
mWifiEntry = connectWifiEntry;
}
@Override
public void onConnectResult(@ConnectStatus int status) {
if (DEBUG) {
Log.d(TAG, "onConnectResult " + status);
}
if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startActivity(intent, true);
} else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
Toast.makeText(mContext, R.string.wifi_failed_connect_message,
Toast.LENGTH_SHORT).show();
} else {
if (DEBUG) {
Log.d(TAG, "connect failure reason=" + status);
}
}
}
}
void scanWifiAccessPoints() {
mAccessPointController.scanForAccessPoints();
}
@Override
public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
if (accessPoints == null) {
return;
}
boolean hasConnectedWifi = false;
mWifiEntry = accessPoints;
for (WifiEntry wifiEntry : accessPoints) {
if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
mConnectedEntry = wifiEntry;
hasConnectedWifi = true;
break;
}
}
if (!hasConnectedWifi) {
mConnectedEntry = null;
}
mCallback.onAccessPointsChanged(mWifiEntry, mConnectedEntry);
}
@Override
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
@Override
public void onDisconnectResult(int status) {
}
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
TelephonyCallback.ServiceStateListener,
TelephonyCallback.SignalStrengthsListener {
@Override
public void onServiceStateChanged(@NonNull ServiceState serviceState) {
mCallback.onServiceStateChanged(serviceState);
}
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
mCallback.onDataConnectionStateChanged(state, networkType);
}
@Override
public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
mCallback.onSignalStrengthsChanged(signalStrength);
}
@Override
public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
mTelephonyDisplayInfo = telephonyDisplayInfo;
mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
}
}
private class InternetOnSubscriptionChangedListener
extends SubscriptionManager.OnSubscriptionsChangedListener {
InternetOnSubscriptionChangedListener() {
super();
}
@Override
public void onSubscriptionsChanged() {
mDefaultDataSubId = getDefaultDataSubscriptionId();
if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
mTelephonyManager.registerTelephonyCallback(mHandler::post,
mInternetTelephonyCallback);
mCallback.onSubscriptionsChanged(mDefaultDataSubId);
}
}
}
private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
@Override
public void onCapabilitiesChanged(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities) {
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork != null && activeNetwork.equals(network)) {
// update UI
mCallback.onCapabilitiesChanged(network, networkCapabilities);
}
}
}
private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mCallback.onWifiStateReceived(context, intent);
}
};
interface InternetDialogCallback {
void onRefreshCarrierInfo();
void onSimStateChanged();
void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
void onSubscriptionsChanged(int defaultDataSubId);
void onServiceStateChanged(ServiceState serviceState);
void onDataConnectionStateChanged(int state, int networkType);
void onSignalStrengthsChanged(SignalStrength signalStrength);
void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
void dismissDialog();
void onAccessPointsChanged(List<WifiEntry> wifiEntryList, WifiEntry connectedEntry);
void onWifiStateReceived(Context context, Intent intent);
}
}

View File

@@ -0,0 +1,14 @@
package com.android.systemui.qs.tiles.dialog;
import android.content.Context;
import android.util.FeatureFlagUtils;
public class InternetDialogUtil {
public static boolean isProviderModelEnabled(Context context) {
if (context == null) {
return false;
}
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
}

View File

@@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
@@ -156,6 +157,15 @@ public class AccessPointControllerImpl
fireAcccessPointsCallback(entries);
}
@Override
public MergedCarrierEntry getMergedCarrierEntry() {
if (mWifiPickerTracker == null) {
fireAcccessPointsCallback(Collections.emptyList());
return null;
}
return mWifiPickerTracker.getMergedCarrierEntry();
}
@Override
public int getIcon(WifiEntry ap) {
int level = ap.getLevel();

View File

@@ -23,6 +23,7 @@ import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -223,6 +224,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
MergedCarrierEntry getMergedCarrierEntry();
int getIcon(WifiEntry ap);
boolean connect(WifiEntry ap);
boolean canConfigWifi();

View File

@@ -0,0 +1,269 @@
package com.android.systemui.qs.tiles.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wifitrackerlib.WifiEntry;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
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;
import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class InternetDialogControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private MockInternetDialogController mInternetDialogController;
private InternetDialogController.InternetDialogCallback mCallback =
mock(InternetDialogController.InternetDialogCallback.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
private WifiManager mWifiManager = mock(WifiManager.class);
private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
private Handler mHandler;
@Mock
private GlobalSettings mGlobalSettings;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private NetworkController.AccessPointController mAccessPointController;
@Mock
private WifiEntry mWifiEntryConnected = mock(WifiEntry.class);
@Mock
private WifiInfo mWifiInfo;
@Mock
private ServiceState mServiceState;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
when(mWifiManager.getConnectionInfo()).thenReturn(mWifiInfo);
mInternetDialogController = new MockInternetDialogController(mContext, mUiEventLogger,
mStarter, mAccessPointController, mSubscriptionManager, mTelephonyManager,
mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
mKeyguardUpdateMonitor, mGlobalSettings);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mCallback);
}
@Test
public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
mInternetDialogController.setAirplaneModeEnabled(true);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("airplane_mode")));
}
@Test
public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
mInternetDialogController.setAirplaneModeEnabled(false);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("quick_settings_internet_label")));
}
@Test
public void getSubtitleText_withAirplaneModeOn_returnNull() {
mInternetDialogController.setAirplaneModeEnabled(true);
assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
}
@Test
public void getSubtitleText_withWifiOff_returnWifiIsOff() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(false);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("wifi_is_off")));
}
@Test
public void getSubtitleText_withWifiOn_returnSearchWifi() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(true),
getResourcesString("wifi_empty_list_wifi_on")));
}
@Test
public void getSubtitleText_withWifiEntry_returnTapToConnect() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = mock(ArrayList.class);
doReturn(1).when(wifiScanResults).size();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("tap_a_network_to_connect")));
}
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = new ArrayList<>();
doReturn(wifiScanResults).when(mWifiManager).getScanResults();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
when(mSubscriptionManager.getActiveSubscriptionIdList())
.thenReturn(new int[] {SUB_ID});
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
}
@Test
public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
mInternetDialogController.setAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
List<ScanResult> wifiScanResults = new ArrayList<>();
doReturn(wifiScanResults).when(mWifiManager).getScanResults();
when(mWifiManager.getScanResults()).thenReturn(wifiScanResults);
when(mSubscriptionManager.getActiveSubscriptionIdList())
.thenReturn(new int[] {SUB_ID});
doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
getResourcesString("non_carrier_network_unavailable")));
}
@Test
public void getConnectedWifiTitle_withNoConnectedEntry_returnNull() {
mInternetDialogController.setConnectedWifiEntry(null);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
""));
}
@Test
public void getConnectedWifiTitle_withConnectedEntry_returnTitle() {
mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
when(mWifiEntryConnected.getTitle()).thenReturn(CONNECTED_TITLE);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiTitle(),
CONNECTED_TITLE));
}
@Test
public void getConnectedWifiSummary_withNoConnectedEntry_returnNull() {
mInternetDialogController.setConnectedWifiEntry(null);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
""));
}
@Test
public void getConnectedWifiSummary_withConnectedEntry_returnSummary() {
mInternetDialogController.setConnectedWifiEntry(mWifiEntryConnected);
when(mWifiEntryConnected.getSummary(false)).thenReturn(CONNECTED_SUMMARY);
assertTrue(TextUtils.equals(mInternetDialogController.getConnectedWifiSummary(),
CONNECTED_SUMMARY));
}
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
private int getResourcesId(String name) {
return mContext.getResources().getIdentifier(name, "string",
mContext.getPackageName());
}
private class MockInternetDialogController extends InternetDialogController {
private WifiEntry mConnectedEntry;
private GlobalSettings mGlobalSettings;
private boolean mIsAirplaneModeOn;
MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
ActivityStarter starter, AccessPointController accessPointController,
SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings) {
super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
broadcastDispatcher, keyguardUpdateMonitor, globalSettings);
mGlobalSettings = globalSettings;
}
@Override
boolean isAirplaneModeEnabled() {
return mIsAirplaneModeOn;
}
public void setAirplaneModeEnabled(boolean enabled) {
mIsAirplaneModeOn = enabled;
}
@Override
WifiEntry getConnectedWifiEntry() {
return mConnectedEntry;
}
public void setConnectedWifiEntry(WifiEntry connectedEntry) {
mConnectedEntry = connectedEntry;
}
}
}