diff --git a/res/layout/data_usage_summary_preference.xml b/res/layout/data_usage_summary_preference.xml
new file mode 100644
index 00000000000..445e7cdd468
--- /dev/null
+++ b/res/layout/data_usage_summary_preference.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2540640f901..72db2537492 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5844,6 +5844,8 @@
2G-3G data
4G data
+
+ Roaming
Foreground:
@@ -8655,6 +8657,27 @@
- %1$d apps allowed to use unrestricted data when Data Saver is on
+
+ Primary data
+
+
+ ^1 used
+
+
+ , ^1 left
+
+
+ %1$s left in this cycle
+
+
+ Updated by %1$s %2$s
+
+
+ Updated %1$s
+
+
+ VIEW PLAN
+
Data saver
diff --git a/res/xml/data_usage.xml b/res/xml/data_usage.xml
index 958459c2d16..88d7f3202b7 100644
--- a/res/xml/data_usage.xml
+++ b/res/xml/data_usage.xml
@@ -20,24 +20,8 @@
android:key="data_usage_screen"
android:title="@string/data_usage_summary_title">
-
-
-
-
-
-
-
-
-
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index b63cee3cdad..339de7c022b 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -18,7 +18,6 @@ import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.UserManager;
@@ -28,6 +27,7 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
import android.text.BidiFormatter;
import android.text.Spannable;
import android.text.SpannableString;
@@ -40,7 +40,6 @@ import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.SummaryPreference;
import com.android.settings.Utils;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -65,7 +64,6 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
private static final String KEY_STATUS_HEADER = "status_header";
- private static final String KEY_LIMIT_SUMMARY = "limit_summary";
// Mobile data keys
public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
@@ -77,13 +75,9 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
- private DataUsageController mDataUsageController;
- private DataUsageInfoController mDataInfoController;
- private SummaryPreference mSummaryPreference;
- private Preference mLimitPreference;
+ private DataUsageSummaryPreference mSummaryPreference;
+ private DataUsageSummaryPreferenceController mSummaryController;
private NetworkTemplate mDefaultTemplate;
- private int mDataUsageTemplate;
- private NetworkPolicyEditor mPolicyEditor;
@Override
public int getHelpResource() {
@@ -95,25 +89,20 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
super.onCreate(icicle);
final Context context = getContext();
- NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
- mPolicyEditor = new NetworkPolicyEditor(policyManager);
boolean hasMobileData = DataUsageUtils.hasMobileData(context);
- mDataUsageController = new DataUsageController(context);
- mDataInfoController = new DataUsageInfoController();
int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
hasMobileData = false;
}
mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
- mSummaryPreference = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
+ mSummaryPreference = (DataUsageSummaryPreference) findPreference(KEY_STATUS_HEADER);
if (!hasMobileData || !isAdmin()) {
removePreference(KEY_RESTRICT_BACKGROUND);
}
if (hasMobileData) {
- mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
List subscriptions =
services.mSubscriptionManager.getActiveSubscriptionInfoList();
if (subscriptions == null || subscriptions.size() == 0) {
@@ -127,10 +116,6 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
addMobileSection(subInfo.getSubscriptionId());
}
}
- mSummaryPreference.setSelectable(true);
- } else {
- removePreference(KEY_LIMIT_SUMMARY);
- mSummaryPreference.setSelectable(false);
}
boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
if (hasWifiRadio) {
@@ -139,10 +124,6 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
if (hasEthernet(context)) {
addEthernetSection();
}
- mDataUsageTemplate = hasMobileData ? R.string.cell_data_template
- : hasWifiRadio ? R.string.wifi_data_template
- : R.string.ethernet_data_template;
-
setHasOptionsMenu(true);
}
@@ -189,7 +170,11 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
@Override
protected List getPreferenceControllers(Context context) {
- return null;
+ final ArrayList controllers = new ArrayList<>();
+ mSummaryController =
+ new DataUsageSummaryPreferenceController(context);
+ controllers.add(mSummaryController);
+ return controllers;
}
private void addMobileSection(int subId) {
@@ -269,36 +254,6 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
}
private void updateState() {
- DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
- mDefaultTemplate);
- Context context = getContext();
- mDataInfoController.updateDataLimit(info,
- services.mPolicyEditor.getPolicy(mDefaultTemplate));
-
- if (mSummaryPreference != null) {
- mSummaryPreference.setTitle(
- formatUsage(context, getString(mDataUsageTemplate), info.usageLevel));
- final long limit = mDataInfoController.getSummaryLimit(info);
- mSummaryPreference.setSummary(info.period);
- if (limit <= 0) {
- mSummaryPreference.setChartEnabled(false);
- } else {
- mSummaryPreference.setChartEnabled(true);
- mSummaryPreference.setLabels(Formatter.formatFileSize(context, 0),
- Formatter.formatFileSize(context, limit));
- mSummaryPreference.setRatios(info.usageLevel / (float) limit, 0,
- (limit - info.usageLevel) / (float) limit);
- }
- }
- if (mLimitPreference != null && (info.warningLevel > 0 || info.limitLevel > 0)) {
- String warning = Formatter.formatFileSize(context, info.warningLevel);
- String limit = Formatter.formatFileSize(context, info.limitLevel);
- mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only
- : R.string.cell_warning_and_limit, warning, limit));
- } else if (mLimitPreference != null) {
- mLimitPreference.setSummary(null);
- }
-
PreferenceScreen screen = getPreferenceScreen();
for (int i = 1; i < screen.getPreferenceCount(); i++) {
((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
@@ -323,6 +278,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
@Override
public void updateDataUsage() {
updateState();
+ mSummaryController.updateState(mSummaryPreference);
}
private static class SummaryProvider
@@ -341,17 +297,39 @@ public class DataUsageSummary extends DataUsageBaseFragment implements Indexable
@Override
public void setListening(boolean listening) {
if (listening) {
- DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
- String used;
- if (info == null) {
- used = Formatter.formatFileSize(mActivity, 0);
- } else if (info.limitLevel <= 0) {
- used = Formatter.formatFileSize(mActivity, info.usageLevel);
- } else {
- used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
- }
mSummaryLoader.setSummary(this,
- mActivity.getString(R.string.data_usage_summary_format, used));
+ mActivity.getString(R.string.data_usage_summary_format, formatUsedData()));
+ }
+ }
+
+ private String 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 Formatter.formatFileSize(mActivity, dfltPlan.getDataUsageBytes());
+ } else {
+ return Utils.formatPercentage(dfltPlan.getDataUsageBytes(),
+ dfltPlan.getDataLimitBytes());
+ }
+ }
+
+ private String formatFallbackData() {
+ DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
+ if (info == null) {
+ return Formatter.formatFileSize(mActivity, 0);
+ } else if (info.limitLevel <= 0) {
+ return Formatter.formatFileSize(mActivity, info.usageLevel);
+ } else {
+ return Utils.formatPercentage(info.usageLevel, info.limitLevel);
}
}
}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
new file mode 100644
index 00000000000..984df023507
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.SummaryPreference;
+import com.android.settingslib.utils.StringUtil;
+
+import libcore.util.Objects;
+
+/**
+ * Provides a summary of data usage.
+ */
+public class DataUsageSummaryPreference extends SummaryPreference {
+
+ private int mNumPlans;
+ /** The ending time of the billing cycle in milliseconds since epoch. */
+ private long mCycleEndTimeMs;
+ /** The time of the last update in standard milliseconds since the epoch */
+ private long mSnapshotTimeMs;
+ /** Name of carrier, or null if not available */
+ private CharSequence mCarrierName;
+ private String mLimitInfoText;
+ private Intent mLaunchIntent;
+
+ public DataUsageSummaryPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.data_usage_summary_preference);
+ }
+
+ public void setLimitInfo(String text) {
+ if (!Objects.equal(text, mLimitInfoText)) {
+ mLimitInfoText = text;
+ notifyChanged();
+ }
+ }
+
+ public void setUsageInfo(long cycleEnd, long snapshotTime, CharSequence carrierName,
+ int numPlans, Intent launchIntent) {
+ mCycleEndTimeMs = cycleEnd;
+ mSnapshotTimeMs = snapshotTime;
+ mCarrierName = carrierName;
+ mNumPlans = numPlans;
+ mLaunchIntent = launchIntent;
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ TextView usageTitle = (TextView) holder.findViewById(R.id.usage_title);
+ usageTitle.setVisibility(mNumPlans > 1 ? View.VISIBLE : View.GONE);
+
+ TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
+ cycleTime.setText(getContext().getString(R.string.cycle_left_time_text,
+ StringUtil.formatElapsedTime(getContext(),
+ mCycleEndTimeMs - System.currentTimeMillis(),false /* withSeconds */)));
+
+ TextView carrierInfo = (TextView) holder.findViewById(R.id.carrier_and_update);
+ setCarrierInfo(carrierInfo, mCarrierName, mSnapshotTimeMs);
+
+ Button launchButton = (Button) holder.findViewById(R.id.launch_mdp_app_button);
+ launchButton.setOnClickListener((view) -> {
+ getContext().sendBroadcast(mLaunchIntent);
+ });
+ if (mLaunchIntent != null) {
+ launchButton.setVisibility(View.VISIBLE);
+ } else {
+ launchButton.setVisibility(View.GONE);
+ }
+
+ TextView limitInfo = (TextView) holder.findViewById(R.id.data_limits);
+ limitInfo.setVisibility(
+ mLimitInfoText == null || mLimitInfoText.isEmpty() ? View.GONE : View.VISIBLE);
+ limitInfo.setText(mLimitInfoText);
+ }
+
+ private void setCarrierInfo(TextView carrierInfo, CharSequence carrierName, long updateAge) {
+ if (mNumPlans > 0 && updateAge >= 0L) {
+ carrierInfo.setVisibility(View.VISIBLE);
+ if (carrierName != null) {
+ carrierInfo.setText(getContext().getString(R.string.carrier_and_update_text,
+ carrierName, StringUtil.formatRelativeTime(
+ getContext(), updateAge, false /* withSeconds */)));
+ } else {
+ carrierInfo.setText(getContext().getString(R.string.no_carrier_update_text,
+ StringUtil.formatRelativeTime(
+ getContext(), updateAge, false /* withSeconds */)));
+ }
+ } else {
+ carrierInfo.setVisibility(View.GONE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
new file mode 100644
index 00000000000..11da829ad61
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2018 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.content.Context;
+import android.content.Intent;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+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 android.util.Log;
+import android.util.RecurrenceRule;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.DataUsageController;
+
+import java.util.List;
+
+/**
+ * This is the controller for the top of the data usage screen that retrieves carrier data from the
+ * new subscriptions framework API if available. The controller reads subscription information from
+ * the framework and falls back to legacy usage data if none are available.
+ */
+public class DataUsageSummaryPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "DataUsageController";
+ private static final String KEY = "status_header";
+ private static final long PETA = 1000000000000000L;
+ private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f; // (1/0.8)^2
+ private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE; // 0.8^2
+
+ private final DataUsageController mDataUsageController;
+ private final DataUsageInfoController mDataInfoController;
+ private final NetworkTemplate mDefaultTemplate;
+ private final NetworkPolicyEditor mPolicyEditor;
+ private final int mDataUsageTemplate;
+ private final boolean mHasMobileData;
+ private final SubscriptionManager mSubscriptionManager;
+
+ /** Name of the carrier, or null if not available */
+ private CharSequence mCarrierName;
+
+ /** The number of registered plans, [0,N] */
+ private int mDataplanCount;
+
+ /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
+ private long mSnapshotTime;
+
+ /**
+ * The size of the first registered plan if one exists or the size of the warning if it is set.
+ * -1 if no information is available.
+ */
+ private long mDataplanSize;
+ /** The number of bytes used since the start of the cycle. */
+ private long mDataplanUse;
+ /** The starting time of the billing cycle in ms since the epoch */
+ private long mCycleStart;
+ /** The ending time of the billing cycle in ms since the epoch */
+ private long mCycleEnd;
+
+ private Intent mManageSubscriptionIntent;
+
+ public DataUsageSummaryPreferenceController(Context context) {
+ super(context, KEY);
+
+ final int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
+ mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
+ NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
+ mPolicyEditor = new NetworkPolicyEditor(policyManager);
+
+ mHasMobileData = DataUsageUtils.hasMobileData(context)
+ && defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ mDataUsageController = new DataUsageController(context);
+ mDataInfoController = new DataUsageInfoController();
+
+ if (mHasMobileData) {
+ mDataUsageTemplate = R.string.cell_data_template;
+ } else if (DataUsageUtils.hasWifiRadio(context)) {
+ mDataUsageTemplate = R.string.wifi_data_template;
+ } else {
+ mDataUsageTemplate = R.string.ethernet_data_template;
+ }
+
+ mSubscriptionManager = (SubscriptionManager)
+ mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ }
+
+ @VisibleForTesting
+ DataUsageSummaryPreferenceController(
+ Context context,
+ DataUsageController dataUsageController,
+ DataUsageInfoController dataInfoController,
+ NetworkTemplate defaultTemplate,
+ NetworkPolicyEditor policyEditor,
+ int dataUsageTemplate,
+ boolean hasMobileData,
+ SubscriptionManager subscriptionManager) {
+ super(context, KEY);
+ mDataUsageController = dataUsageController;
+ mDataInfoController = dataInfoController;
+ mDefaultTemplate = defaultTemplate;
+ mPolicyEditor = policyEditor;
+ mDataUsageTemplate = dataUsageTemplate;
+ mHasMobileData = hasMobileData;
+ mSubscriptionManager = subscriptionManager;
+ }
+
+ @VisibleForTesting
+ void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) {
+ mDataplanCount = dataPlanCount;
+ mDataplanSize = dataPlanSize;
+ mDataplanUse = dataPlanUse;
+ }
+
+ @VisibleForTesting
+ void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) {
+ mCarrierName = carrierName;
+ mSnapshotTime = snapshotTime;
+ mCycleEnd = cycleEnd;
+ mManageSubscriptionIntent = intent;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
+ DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
+ mDefaultTemplate);
+
+ mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
+
+ if (mSubscriptionManager != null) {
+ refreshDataplanInfo(info);
+ }
+
+ if (mDataplanCount == 0 && (info.warningLevel > 0 || info.limitLevel > 0)) {
+ final String warning = Formatter.formatFileSize(mContext, info.warningLevel);
+ final String limit = Formatter.formatFileSize(mContext, info.limitLevel);
+ summaryPreference.setLimitInfo(mContext.getString(info.limitLevel <= 0
+ ? R.string.cell_warning_only
+ : R.string.cell_warning_and_limit, warning, limit));
+ } else {
+ summaryPreference.setLimitInfo(null);
+ }
+
+ final StringBuilder title = new StringBuilder();
+ if (mHasMobileData) {
+ title.append(formatUsage(mContext, mContext.getString(R.string.data_used),
+ mDataplanUse));
+ if (mDataplanCount >= 0 && mDataplanSize > 0L) {
+ title.append(formatUsage(mContext, mContext.getString(R.string.data_remaining),
+ mDataplanSize - mDataplanUse));
+ }
+ } else {
+ title.append(formatUsage(mContext, mContext.getString(mDataUsageTemplate),
+ mDataplanUse));
+ }
+ summaryPreference.setTitle(title.toString());
+
+ if (mDataplanSize <= 0) {
+ summaryPreference.setChartEnabled(false);
+ } else {
+ summaryPreference.setChartEnabled(true);
+ summaryPreference.setLabels(Formatter.formatFileSize(mContext, 0 /* sizeBytes */),
+ Formatter.formatFileSize(mContext, mDataplanSize));
+ summaryPreference.setRatios(mDataplanUse / (float) mDataplanSize, 0 /* middle */,
+ (mDataplanSize - mDataplanUse) / (float) mDataplanSize);
+ }
+ summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName,
+ mDataplanCount, mManageSubscriptionIntent);
+ }
+
+ // TODO(b/70950124) add test for this method once the robolectric shadow run script is
+ // completed (b/3526807)
+ private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) {
+ // reset data before overwriting
+ mCarrierName = null;
+ mDataplanCount = 0;
+ mDataplanSize = mDataInfoController.getSummaryLimit(info);
+ mDataplanUse = info.usageLevel;
+ mCycleStart = info.cycleStart;
+ mCycleEnd = info.cycleEnd;
+ mSnapshotTime = -1L;
+
+ final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ final SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+ if (subInfo != null && mHasMobileData) {
+ mCarrierName = subInfo.getCarrierName();
+ List plans = mSubscriptionManager.getSubscriptionPlans(defaultSubId);
+ final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager, defaultSubId);
+ if (primaryPlan != null) {
+ mDataplanCount = plans.size();
+ mDataplanSize = primaryPlan.getDataLimitBytes();
+ if (unlimited(mDataplanSize)) {
+ mDataplanSize = 0L;
+ }
+ mDataplanUse = primaryPlan.getDataUsageBytes();
+
+ RecurrenceRule rule = primaryPlan.getCycleRule();
+ if (rule != null && rule.start != null && rule.end != null) {
+ mCycleStart = rule.start.toEpochSecond() * 1000L;
+ mCycleEnd = rule.end.toEpochSecond() * 1000L;
+ }
+ mSnapshotTime = System.currentTimeMillis() - primaryPlan.getDataUsageTime();
+ }
+ }
+ mManageSubscriptionIntent =
+ mSubscriptionManager.createManageSubscriptionIntent(defaultSubId);
+ Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + defaultSubId
+ + ", intent " + mManageSubscriptionIntent);
+ }
+
+ public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) {
+ List plans = subManager.getSubscriptionPlans(primaryId);
+ if (CollectionUtils.isEmpty(plans)) {
+ return null;
+ }
+ // First plan in the list is the primary plan
+ SubscriptionPlan plan = plans.get(0);
+ return plan.getDataLimitBytes() > 0
+ && saneSize(plan.getDataUsageBytes())
+ && plan.getCycleRule() != null ? plan : null;
+ }
+
+ private static boolean saneSize(long value) {
+ return value >= 0L && value < PETA;
+ }
+
+ public static boolean unlimited(long size) {
+ return size == SubscriptionPlan.BYTES_UNLIMITED;
+ }
+
+ @VisibleForTesting
+ private static CharSequence formatUsage(Context context, String template, long usageLevel) {
+ final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
+
+ final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
+ usageLevel, Formatter.FLAG_CALCULATE_ROUNDED);
+ final SpannableString enlargedValue = new SpannableString(usedResult.value);
+ enlargedValue.setSpan(
+ new RelativeSizeSpan(RELATIVE_SIZE_LARGE), 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(RELATIVE_SIZE_SMALL), 0, fullTemplate.length(), FLAGS);
+ return TextUtils.expandTemplate(fullTemplate,
+ BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
new file mode 100644
index 00000000000..ea1d29ba370
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 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.content.Context;
+import android.content.Intent;
+import android.net.NetworkTemplate;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.DataUsageController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DataUsageSummaryPreferenceControllerTest {
+ private static final long UPDATE_BACKOFF_MS = TimeUnit.MINUTES.toMillis(13);
+ private static final long CYCLE_BACKOFF_MS = TimeUnit.DAYS.toMillis(6);
+ private static final long CYCLE_LENGTH_MS = TimeUnit.DAYS.toMillis(30);
+ private static final long USAGE1 = 373000000L;
+ private static final long LIMIT1 = 1000000000L;
+ private static final String CARRIER_NAME = "z-mobile";
+ private static final String PERIOD = "Feb";
+
+ @Mock
+ private DataUsageController mDataUsageController;
+ @Mock
+ private DataUsageInfoController mDataInfoController;
+ @Mock
+ private DataUsageSummaryPreference mSummaryPreference;
+ @Mock
+ private NetworkPolicyEditor mPolicyEditor;
+ @Mock
+ private NetworkTemplate mNetworkTemplate;
+
+ private Context mContext;
+ private DataUsageSummaryPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mController = new DataUsageSummaryPreferenceController(
+ mContext,
+ mDataUsageController,
+ mDataInfoController,
+ mNetworkTemplate,
+ mPolicyEditor,
+ R.string.cell_data_template,
+ true,
+ null);
+ }
+
+ @Test
+ public void testSummaryUpdate_onePlan_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ final Intent intent = new Intent();
+
+ when(mDataUsageController.getDataUsageInfo()).thenReturn(info);
+ mController.setPlanValues(1 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(CARRIER_NAME, now - UPDATE_BACKOFF_MS, info.cycleEnd, intent);
+
+ mController.updateState(mSummaryPreference);
+ verify(mSummaryPreference).setLimitInfo(null);
+ verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
+ CARRIER_NAME, 1 /* numPlans */, intent);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noPlan_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ final Intent intent = new Intent();
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(CARRIER_NAME, now - UPDATE_BACKOFF_MS, info.cycleEnd, intent);
+
+ mController.updateState(mSummaryPreference);
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
+ CARRIER_NAME, 0 /* numPlans */, intent);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noCarrier_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(null /* carrierName */, -1L /* snapshotTime */,
+ info.cycleEnd, null /* intent */);
+ mController.updateState(mSummaryPreference);
+
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(
+ info.cycleEnd,
+ -1L /* snapshotTime */,
+ null /* carrierName */,
+ 0 /* numPlans */,
+ null /* launchIntent */);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noPlanData_basic() {
+ final long now = System.currentTimeMillis();
+
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, -1L /* dataPlanSize */, USAGE1);
+ mController.setCarrierValues(null /* carrierName */, -1L /* snapshotTime */,
+ info.cycleEnd, null /* intent */);
+ mController.updateState(mSummaryPreference);
+
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(
+ info.cycleEnd,
+ -1L /* snapshotTime */,
+ null /* carrierName */,
+ 0 /* numPlans */,
+ null /* launchIntent */);
+ verify(mSummaryPreference).setChartEnabled(false);
+ }
+
+ private DataUsageController.DataUsageInfo createTestDataUsageInfo(long now) {
+ DataUsageController.DataUsageInfo info = new DataUsageController.DataUsageInfo();
+ info.carrier = CARRIER_NAME;
+ info.period = PERIOD;
+ info.startDate = now;
+ info.limitLevel = LIMIT1;
+ info.warningLevel = LIMIT1 >> 1;
+ info.usageLevel = USAGE1;
+ info.cycleStart = now - CYCLE_BACKOFF_MS;
+ info.cycleEnd = info.cycleStart + CYCLE_LENGTH_MS;
+ return info;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
new file mode 100644
index 00000000000..769d9e7e28e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl;
+import com.android.settingslib.utils.StringUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows =
+ SettingsShadowResourcesImpl.class)
+public class DataUsageSummaryPreferenceTest {
+ private static final long CYCLE_DURATION_MILLIS = 1000000000L;
+ private static final long UPDATE_LAG_MILLIS = 10000000L;
+ private static final String DUMMY_CARRIER = "z-mobile";
+
+ private Context mContext;
+ private PreferenceViewHolder mHolder;
+ private DataUsageSummaryPreference mSummaryPreference;
+ private TextView mUsageTitle;
+ private TextView mCycleTime;
+ private TextView mCarrierInfo;
+ private TextView mDataLimits;
+ private Button mLaunchButton;
+
+ private long mCycleEnd;
+ private long mUpdateTime;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mSummaryPreference = new DataUsageSummaryPreference(mContext, null /* attrs */);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(mSummaryPreference.getLayoutResource(), null /* root */,
+ false /* attachToRoot */);
+
+ mHolder = PreferenceViewHolder.createInstanceForTests(view);
+
+ final long now = System.currentTimeMillis();
+ mCycleEnd = now + CYCLE_DURATION_MILLIS;
+ mUpdateTime = now - UPDATE_LAG_MILLIS;
+ }
+
+ @Test
+ public void testSetUsageInfo_withLaunchIntent_launchButtonShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mLaunchButton.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withoutLaunchIntent_launchButtonNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ null /* launchIntent */);
+
+ bindViewHolder();
+ assertThat(mLaunchButton.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withDataPlans_carrierInfoShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withNoDataPlans_carrierInfoNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mUsageTitle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withMultipleDataPlans_usageTitleShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 2 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mUsageTitle.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_cycleRemainingTimeShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+ String cyclePrefix = StringUtil.formatElapsedTime(mContext, CYCLE_DURATION_MILLIS,
+ false /* withSeconds */).toString();
+ String text = mContext.getString(R.string.cycle_left_time_text, cyclePrefix);
+
+ bindViewHolder();
+ assertThat(mCycleTime.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCycleTime.getText()).isEqualTo(text);
+ }
+
+ @Test
+ public void testSetLimitInfo_withLimitInfo_dataLimitsShown() {
+ final String limitText = "test limit text";
+ mSummaryPreference.setLimitInfo(limitText);
+
+ bindViewHolder();
+ assertThat(mDataLimits.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mDataLimits.getText()).isEqualTo(limitText);
+ }
+
+ @Test
+ public void testSetLimitInfo_withNullLimitInfo_dataLimitsNotShown() {
+ mSummaryPreference.setLimitInfo(null);
+
+ bindViewHolder();
+ assertThat(mDataLimits.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ private void bindViewHolder() {
+ mSummaryPreference.onBindViewHolder(mHolder);
+ mUsageTitle = (TextView) mHolder.findViewById(R.id.usage_title);
+ mCycleTime = (TextView) mHolder.findViewById(R.id.cycle_left_time);
+ mCarrierInfo = (TextView) mHolder.findViewById(R.id.carrier_and_update);
+ mDataLimits = (TextView) mHolder.findViewById(R.id.data_limits);
+ mLaunchButton = (Button) mHolder.findViewById(R.id.launch_mdp_app_button);
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
index 6c6d7ab64c6..260e3ae062d 100644
--- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
@@ -137,7 +137,6 @@ public class BaseSearchIndexProviderTest {
final List nonIndexableKeys = provider
.getNonIndexableKeys(RuntimeEnvironment.application);
- assertThat(nonIndexableKeys).containsAllOf("status_header", "limit_summary",
- "restrict_background");
+ assertThat(nonIndexableKeys).contains("status_header");
}
}