- On the Data usage settings page, we check for valid subscription id to determine if we should show the mobile section. However, we are querying the default subscription id, which will return the 1st id for any subscription if there's no data subscription. Since this is for mobile section, we should query the default data subscription id explicitly. Change-Id: I08c80639d07477e486a2d9a10761e11df4c73b28 Fixes: 119881046 Test: make RunSettingsRoboTests
389 lines
15 KiB
Java
389 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2016 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.datausage;
|
|
|
|
import android.app.Activity;
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.Context;
|
|
import android.net.NetworkTemplate;
|
|
import android.os.Bundle;
|
|
import android.provider.SearchIndexableResource;
|
|
import android.telephony.SubscriptionInfo;
|
|
import android.telephony.SubscriptionManager;
|
|
import android.telephony.SubscriptionPlan;
|
|
import android.text.BidiFormatter;
|
|
import android.text.Spannable;
|
|
import android.text.SpannableString;
|
|
import android.text.TextUtils;
|
|
import android.text.format.Formatter;
|
|
import android.text.style.RelativeSizeSpan;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.dashboard.SummaryLoader;
|
|
import com.android.settings.search.BaseSearchIndexProvider;
|
|
import com.android.settings.search.Indexable;
|
|
import com.android.settingslib.NetworkPolicyEditor;
|
|
import com.android.settingslib.core.AbstractPreferenceController;
|
|
import com.android.settingslib.net.DataUsageController;
|
|
import com.android.settingslib.search.SearchIndexable;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Settings preference fragment that displays data usage summary.
|
|
*/
|
|
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
|
public class DataUsageSummary extends DataUsageBaseFragment implements DataUsageEditController {
|
|
|
|
private static final String TAG = "DataUsageSummary";
|
|
|
|
static final boolean LOGD = false;
|
|
|
|
public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
|
|
|
|
private static final String KEY_STATUS_HEADER = "status_header";
|
|
|
|
// Mobile data keys
|
|
public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
|
|
public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable";
|
|
public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage";
|
|
public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference";
|
|
|
|
// Wifi keys
|
|
public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
|
|
public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
|
|
|
|
private DataUsageSummaryPreference mSummaryPreference;
|
|
private DataUsageSummaryPreferenceController mSummaryController;
|
|
private NetworkTemplate mDefaultTemplate;
|
|
|
|
@Override
|
|
public int getHelpResource() {
|
|
return R.string.help_url_data_usage;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
Context context = getContext();
|
|
|
|
boolean hasMobileData = DataUsageUtils.hasMobileData(context);
|
|
|
|
final int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
|
|
if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
|
hasMobileData = false;
|
|
}
|
|
mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
|
|
mSummaryPreference = (DataUsageSummaryPreference) findPreference(KEY_STATUS_HEADER);
|
|
|
|
if (!hasMobileData || !isAdmin()) {
|
|
removePreference(KEY_RESTRICT_BACKGROUND);
|
|
}
|
|
boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
|
|
if (hasMobileData) {
|
|
addMobileSection(defaultSubId);
|
|
if (DataUsageUtils.hasSim(context) && hasWifiRadio) {
|
|
// If the device has a SIM installed, the data usage section shows usage for mobile,
|
|
// and the WiFi section is added if there is a WiFi radio - legacy behavior.
|
|
addWifiSection();
|
|
}
|
|
// Do not add the WiFi section if either there is no WiFi radio (obviously) or if no
|
|
// SIM is installed. In the latter case the data usage section will show WiFi usage and
|
|
// there should be no explicit WiFi section added.
|
|
} else if (hasWifiRadio) {
|
|
addWifiSection();
|
|
}
|
|
if (DataUsageUtils.hasEthernet(context)) {
|
|
addEthernetSection();
|
|
}
|
|
setHasOptionsMenu(true);
|
|
}
|
|
|
|
@Override
|
|
public boolean onPreferenceTreeClick(Preference preference) {
|
|
if (preference == findPreference(KEY_STATUS_HEADER)) {
|
|
BillingCycleSettings.BytesEditorFragment.show(this, false);
|
|
return false;
|
|
}
|
|
return super.onPreferenceTreeClick(preference);
|
|
}
|
|
|
|
@Override
|
|
protected int getPreferenceScreenResId() {
|
|
return R.xml.data_usage;
|
|
}
|
|
|
|
@Override
|
|
protected String getLogTag() {
|
|
return TAG;
|
|
}
|
|
|
|
@Override
|
|
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
|
final Activity activity = getActivity();
|
|
final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
|
|
mSummaryController =
|
|
new DataUsageSummaryPreferenceController(activity, getSettingsLifecycle(), this,
|
|
DataUsageUtils.getDefaultSubscriptionId(activity));
|
|
controllers.add(mSummaryController);
|
|
getSettingsLifecycle().addObserver(mSummaryController);
|
|
return controllers;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void addMobileSection(int subId) {
|
|
addMobileSection(subId, null);
|
|
}
|
|
|
|
private void addMobileSection(int subId, SubscriptionInfo subInfo) {
|
|
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
|
|
inflatePreferences(R.xml.data_usage_cellular);
|
|
category.setTemplate(getNetworkTemplate(subId), subId, services);
|
|
category.pushTemplates(services);
|
|
if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) {
|
|
Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE);
|
|
title.setTitle(subInfo.getDisplayName());
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void addWifiSection() {
|
|
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
|
|
inflatePreferences(R.xml.data_usage_wifi);
|
|
category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services);
|
|
}
|
|
|
|
private void addEthernetSection() {
|
|
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
|
|
inflatePreferences(R.xml.data_usage_ethernet);
|
|
category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
|
|
}
|
|
|
|
private Preference inflatePreferences(int resId) {
|
|
PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
|
|
getPrefContext(), resId, null);
|
|
Preference pref = rootPreferences.getPreference(0);
|
|
rootPreferences.removeAll();
|
|
|
|
PreferenceScreen screen = getPreferenceScreen();
|
|
pref.setOrder(screen.getPreferenceCount());
|
|
screen.addPreference(pref);
|
|
|
|
return pref;
|
|
}
|
|
|
|
private NetworkTemplate getNetworkTemplate(int subscriptionId) {
|
|
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
|
|
services.mTelephonyManager.getSubscriberId(subscriptionId));
|
|
return NetworkTemplate.normalize(mobileAll,
|
|
services.mTelephonyManager.getMergedSubscriberIds());
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
updateState();
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static CharSequence formatUsage(Context context, String template, long usageLevel) {
|
|
final float LARGER_SIZE = 1.25f * 1.25f; // (1/0.8)^2
|
|
final float SMALLER_SIZE = 1.0f / LARGER_SIZE; // 0.8^2
|
|
return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE);
|
|
}
|
|
|
|
static CharSequence formatUsage(Context context, String template, long usageLevel,
|
|
float larger, float smaller) {
|
|
final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
|
|
|
final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
|
|
usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS);
|
|
final SpannableString enlargedValue = new SpannableString(usedResult.value);
|
|
enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS);
|
|
|
|
final SpannableString amountTemplate = new SpannableString(
|
|
context.getString(com.android.internal.R.string.fileSizeSuffix)
|
|
.replace("%1$s", "^1").replace("%2$s", "^2"));
|
|
final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
|
|
enlargedValue, usedResult.units);
|
|
|
|
final SpannableString fullTemplate = new SpannableString(template);
|
|
fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS);
|
|
return TextUtils.expandTemplate(fullTemplate,
|
|
BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
|
|
}
|
|
|
|
private void updateState() {
|
|
PreferenceScreen screen = getPreferenceScreen();
|
|
for (int i = 1; i < screen.getPreferenceCount(); i++) {
|
|
Preference currentPreference = screen.getPreference(i);
|
|
if (currentPreference instanceof TemplatePreferenceCategory) {
|
|
((TemplatePreferenceCategory) currentPreference).pushTemplates(services);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return SettingsEnums.DATA_USAGE_SUMMARY;
|
|
}
|
|
|
|
@Override
|
|
public NetworkPolicyEditor getNetworkPolicyEditor() {
|
|
return services.mPolicyEditor;
|
|
}
|
|
|
|
@Override
|
|
public NetworkTemplate getNetworkTemplate() {
|
|
return mDefaultTemplate;
|
|
}
|
|
|
|
@Override
|
|
public void updateDataUsage() {
|
|
updateState();
|
|
mSummaryController.updateState(mSummaryPreference);
|
|
}
|
|
|
|
private static class SummaryProvider
|
|
implements SummaryLoader.SummaryProvider {
|
|
|
|
private final Activity mActivity;
|
|
private final SummaryLoader mSummaryLoader;
|
|
private final DataUsageController mDataController;
|
|
|
|
public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
|
|
mActivity = activity;
|
|
mSummaryLoader = summaryLoader;
|
|
mDataController = new DataUsageController(activity);
|
|
}
|
|
|
|
@Override
|
|
public void setListening(boolean listening) {
|
|
if (listening) {
|
|
if (DataUsageUtils.hasSim(mActivity)) {
|
|
mSummaryLoader.setSummary(this,
|
|
mActivity.getString(R.string.data_usage_summary_format,
|
|
formatUsedData()));
|
|
} else {
|
|
final DataUsageController.DataUsageInfo info =
|
|
mDataController.getWifiDataUsageInfo();
|
|
|
|
if (info == null) {
|
|
mSummaryLoader.setSummary(this, null);
|
|
} else {
|
|
final CharSequence wifiFormat = mActivity
|
|
.getText(R.string.data_usage_wifi_format);
|
|
final CharSequence sizeText =
|
|
DataUsageUtils.formatDataUsage(mActivity, info.usageLevel);
|
|
mSummaryLoader.setSummary(this,
|
|
TextUtils.expandTemplate(wifiFormat, sizeText));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private CharSequence formatUsedData() {
|
|
SubscriptionManager subscriptionManager = (SubscriptionManager) mActivity
|
|
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
int defaultSubId = subscriptionManager.getDefaultSubscriptionId();
|
|
if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
|
return formatFallbackData();
|
|
}
|
|
SubscriptionPlan dfltPlan = DataUsageSummaryPreferenceController
|
|
.getPrimaryPlan(subscriptionManager, defaultSubId);
|
|
if (dfltPlan == null) {
|
|
return formatFallbackData();
|
|
}
|
|
if (DataUsageSummaryPreferenceController.unlimited(dfltPlan.getDataLimitBytes())) {
|
|
return DataUsageUtils.formatDataUsage(mActivity, dfltPlan.getDataUsageBytes());
|
|
} else {
|
|
return Utils.formatPercentage(dfltPlan.getDataUsageBytes(),
|
|
dfltPlan.getDataLimitBytes());
|
|
}
|
|
}
|
|
|
|
private CharSequence formatFallbackData() {
|
|
DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
|
|
if (info == null) {
|
|
return DataUsageUtils.formatDataUsage(mActivity, 0);
|
|
} else if (info.limitLevel <= 0) {
|
|
return DataUsageUtils.formatDataUsage(mActivity, info.usageLevel);
|
|
} else {
|
|
return Utils.formatPercentage(info.usageLevel, info.limitLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
|
|
= SummaryProvider::new;
|
|
|
|
/**
|
|
* For search
|
|
*/
|
|
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
|
new BaseSearchIndexProvider() {
|
|
|
|
@Override
|
|
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
|
|
boolean enabled) {
|
|
List<SearchIndexableResource> resources = new ArrayList<>();
|
|
SearchIndexableResource resource = new SearchIndexableResource(context);
|
|
resource.xmlResId = R.xml.data_usage;
|
|
resources.add(resource);
|
|
|
|
resource = new SearchIndexableResource(context);
|
|
resource.xmlResId = R.xml.data_usage_cellular;
|
|
resources.add(resource);
|
|
|
|
resource = new SearchIndexableResource(context);
|
|
resource.xmlResId = R.xml.data_usage_wifi;
|
|
resources.add(resource);
|
|
|
|
return resources;
|
|
}
|
|
|
|
@Override
|
|
public List<String> getNonIndexableKeys(Context context) {
|
|
List<String> keys = super.getNonIndexableKeys(context);
|
|
|
|
if (!DataUsageUtils.hasMobileData(context)) {
|
|
keys.add(KEY_MOBILE_USAGE_TITLE);
|
|
keys.add(KEY_MOBILE_DATA_USAGE_TOGGLE);
|
|
keys.add(KEY_MOBILE_DATA_USAGE);
|
|
}
|
|
|
|
if (!DataUsageUtils.hasWifiRadio(context)) {
|
|
keys.add(KEY_WIFI_DATA_USAGE);
|
|
}
|
|
|
|
// This title is named Wifi, and will confuse users.
|
|
keys.add(KEY_WIFI_USAGE_TITLE);
|
|
|
|
// Duplicate entry for "Data saver"
|
|
keys.add(KEY_RESTRICT_BACKGROUND);
|
|
// Duplicate entry for "Data warning & limit"
|
|
keys.add(KEY_MOBILE_BILLING_CYCLE);
|
|
|
|
return keys;
|
|
}
|
|
};
|
|
}
|