blob: 9d9bf9c65aeae71e85117857d769344177139b64 [file] [log] [blame]
Jason Monkb37e2882016-01-11 14:27:20 -05001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.settings.datausage;
16
17import android.app.Activity;
Fan Zhang31b21002019-01-16 13:49:47 -080018import android.app.settings.SettingsEnums;
Jason Monkb37e2882016-01-11 14:27:20 -050019import android.content.Context;
Jason Monkb37e2882016-01-11 14:27:20 -050020import android.net.NetworkTemplate;
Jason Monkb37e2882016-01-11 14:27:20 -050021import android.os.Bundle;
Jason Monkb37e2882016-01-11 14:27:20 -050022import android.telephony.SubscriptionInfo;
23import android.telephony.SubscriptionManager;
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -070024import android.text.BidiFormatter;
25import android.text.Spannable;
26import android.text.SpannableString;
Jason Monkaa5a8a62016-06-06 16:39:55 -040027import android.text.TextUtils;
Jason Monkb37e2882016-01-11 14:27:20 -050028import android.text.format.Formatter;
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -070029import android.text.style.RelativeSizeSpan;
Fan Zhangcbc42182017-10-11 14:57:22 -070030
Fan Zhang23f8d592018-08-28 15:11:40 -070031import androidx.annotation.VisibleForTesting;
32import androidx.preference.Preference;
33import androidx.preference.PreferenceScreen;
34
Jason Monkb37e2882016-01-11 14:27:20 -050035import com.android.settings.R;
Bonian Chend40f7f72020-05-29 17:28:34 +080036import com.android.settings.datausage.lib.DataUsageLib;
Bonian Chen5e65da02019-11-08 07:40:35 +080037import com.android.settings.network.ProxySubscriptionManager;
Matthew Fritze9fad2892016-08-12 09:37:43 -070038import com.android.settingslib.NetworkPolicyEditor;
jackqdyulei137ff282018-01-25 10:55:59 -080039import com.android.settingslib.core.AbstractPreferenceController;
Fan Zhangcbc42182017-10-11 14:57:22 -070040
Jason Monkb37e2882016-01-11 14:27:20 -050041import java.util.ArrayList;
42import java.util.List;
43
Rajeev Kumare19cd482017-07-14 19:40:56 -070044/**
45 * Settings preference fragment that displays data usage summary.
Rajeev Kumare19cd482017-07-14 19:40:56 -070046 */
Fan Zhang4560ab72018-07-10 15:46:31 -070047public class DataUsageSummary extends DataUsageBaseFragment implements DataUsageEditController {
jackqdyulei137ff282018-01-25 10:55:59 -080048
49 private static final String TAG = "DataUsageSummary";
Jason Monkb37e2882016-01-11 14:27:20 -050050
Jason Monkb37e2882016-01-11 14:27:20 -050051 static final boolean LOGD = false;
52
Matthew Fritze8b0ed392017-05-11 17:57:08 -070053 public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
Matthew Fritze8b0ed392017-05-11 17:57:08 -070054
Jason Monkb37e2882016-01-11 14:27:20 -050055 private static final String KEY_STATUS_HEADER = "status_header";
Matthew Fritze621c2482017-07-31 09:44:57 -070056
57 // Mobile data keys
58 public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
59 public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable";
60 public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage";
61 public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference";
62
63 // Wifi keys
64 public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
65 public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
Jason Monkb37e2882016-01-11 14:27:20 -050066
Jan Nordqvist4fbe0f82018-01-22 14:43:43 -080067 private DataUsageSummaryPreference mSummaryPreference;
68 private DataUsageSummaryPreferenceController mSummaryController;
Jason Monkb37e2882016-01-11 14:27:20 -050069 private NetworkTemplate mDefaultTemplate;
Bonian Chen5e65da02019-11-08 07:40:35 +080070 private ProxySubscriptionManager mProxySubscriptionMgr;
Jason Monkb37e2882016-01-11 14:27:20 -050071
72 @Override
Fan Zhange0b0e9f2017-11-29 14:55:59 -080073 public int getHelpResource() {
Fan Zhang9b528362016-10-20 09:06:12 -070074 return R.string.help_url_data_usage;
75 }
76
77 @Override
Jason Monkb37e2882016-01-11 14:27:20 -050078 public void onCreate(Bundle icicle) {
79 super.onCreate(icicle);
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -070080 Context context = getContext();
Doris Ling47ec3852017-03-21 14:40:43 -070081
Bonian Chena73cb312020-04-23 13:42:04 +080082 enableProxySubscriptionManager(context);
Bonian Chen5e65da02019-11-08 07:40:35 +080083
Rajeev Kumar9664c1a2017-07-13 17:20:22 -070084 boolean hasMobileData = DataUsageUtils.hasMobileData(context);
Jason Monkb37e2882016-01-11 14:27:20 -050085
Doris Ling9c2556d2019-01-24 14:21:18 -080086 final int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
Jason Monk34de9f62016-01-21 09:43:35 -050087 if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
88 hasMobileData = false;
89 }
Rajeev Kumar9664c1a2017-07-13 17:20:22 -070090 mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
Lei Yuded55a12019-05-28 10:05:44 -070091 mSummaryPreference = findPreference(KEY_STATUS_HEADER);
Matthew Fritze9fad2892016-08-12 09:37:43 -070092
Jason Monkb37e2882016-01-11 14:27:20 -050093 if (!hasMobileData || !isAdmin()) {
94 removePreference(KEY_RESTRICT_BACKGROUND);
95 }
Rajeev Kumar9664c1a2017-07-13 17:20:22 -070096 boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -070097 if (hasMobileData) {
98 addMobileSection(defaultSubId);
Bonian Chen76627e42020-04-24 10:43:42 +080099 if (hasActiveSubscription() && hasWifiRadio) {
100 // If the device has active SIM, the data usage section shows usage for mobile,
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -0700101 // and the WiFi section is added if there is a WiFi radio - legacy behavior.
102 addWifiSection();
103 }
104 // Do not add the WiFi section if either there is no WiFi radio (obviously) or if no
105 // SIM is installed. In the latter case the data usage section will show WiFi usage and
106 // there should be no explicit WiFi section added.
107 } else if (hasWifiRadio) {
Jason Monkb37e2882016-01-11 14:27:20 -0500108 addWifiSection();
109 }
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -0700110 if (DataUsageUtils.hasEthernet(context)) {
Jason Monkb37e2882016-01-11 14:27:20 -0500111 addEthernetSection();
112 }
Jason Monkb37e2882016-01-11 14:27:20 -0500113 setHasOptionsMenu(true);
114 }
115
116 @Override
Matthew Fritze9fad2892016-08-12 09:37:43 -0700117 public boolean onPreferenceTreeClick(Preference preference) {
118 if (preference == findPreference(KEY_STATUS_HEADER)) {
119 BillingCycleSettings.BytesEditorFragment.show(this, false);
120 return false;
121 }
122 return super.onPreferenceTreeClick(preference);
123 }
124
jackqdyulei137ff282018-01-25 10:55:59 -0800125 @Override
126 protected int getPreferenceScreenResId() {
127 return R.xml.data_usage;
128 }
129
130 @Override
131 protected String getLogTag() {
132 return TAG;
133 }
134
135 @Override
Fan Zhangf7843ad2018-02-22 13:51:41 -0800136 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
Jan Nordqvistba1c5342018-03-20 16:21:47 -0700137 final Activity activity = getActivity();
Jan Nordqvist4fbe0f82018-01-22 14:43:43 -0800138 final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
139 mSummaryController =
Antony Sargent625787b2019-01-17 12:59:30 -0800140 new DataUsageSummaryPreferenceController(activity, getSettingsLifecycle(), this,
141 DataUsageUtils.getDefaultSubscriptionId(activity));
Jan Nordqvist4fbe0f82018-01-22 14:43:43 -0800142 controllers.add(mSummaryController);
tmfang27c84de2018-06-28 11:39:05 +0800143 getSettingsLifecycle().addObserver(mSummaryController);
Jan Nordqvist4fbe0f82018-01-22 14:43:43 -0800144 return controllers;
jackqdyulei137ff282018-01-25 10:55:59 -0800145 }
146
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -0700147 @VisibleForTesting
148 void addMobileSection(int subId) {
Yoshinori Hiranoc55ca0b2017-02-27 11:51:10 +0800149 addMobileSection(subId, null);
150 }
151
Bonian Chena73cb312020-04-23 13:42:04 +0800152 @VisibleForTesting
153 void enableProxySubscriptionManager(Context context) {
154 // Enable ProxySubscriptionMgr with Lifecycle support for all controllers
155 // live within this fragment
156 mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context);
157 mProxySubscriptionMgr.setLifecycle(getLifecycle());
158 }
159
Bonian Chen76627e42020-04-24 10:43:42 +0800160 @VisibleForTesting
161 boolean hasActiveSubscription() {
162 final List<SubscriptionInfo> subInfoList =
163 mProxySubscriptionMgr.getActiveSubscriptionsInfo();
164 return ((subInfoList != null) && (subInfoList.size() > 0));
165 }
166
Yoshinori Hiranoc55ca0b2017-02-27 11:51:10 +0800167 private void addMobileSection(int subId, SubscriptionInfo subInfo) {
Jason Monkb37e2882016-01-11 14:27:20 -0500168 TemplatePreferenceCategory category = (TemplatePreferenceCategory)
169 inflatePreferences(R.xml.data_usage_cellular);
Bonian Chend40f7f72020-05-29 17:28:34 +0800170 category.setTemplate(DataUsageLib.getMobileTemplate(getContext(), subId),
Lei Yuded55a12019-05-28 10:05:44 -0700171 subId, services);
Jason Monkd0682d02016-01-28 12:35:31 -0500172 category.pushTemplates(services);
Yoshinori Hiranoc55ca0b2017-02-27 11:51:10 +0800173 if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) {
174 Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE);
175 title.setTitle(subInfo.getDisplayName());
176 }
Jason Monkb37e2882016-01-11 14:27:20 -0500177 }
178
Jan Nordqvist9eb43dd2018-03-26 15:29:44 -0700179 @VisibleForTesting
180 void addWifiSection() {
Jason Monkb37e2882016-01-11 14:27:20 -0500181 TemplatePreferenceCategory category = (TemplatePreferenceCategory)
182 inflatePreferences(R.xml.data_usage_wifi);
183 category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services);
184 }
185
186 private void addEthernetSection() {
187 TemplatePreferenceCategory category = (TemplatePreferenceCategory)
188 inflatePreferences(R.xml.data_usage_ethernet);
189 category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
190 }
191
192 private Preference inflatePreferences(int resId) {
193 PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
194 getPrefContext(), resId, null);
195 Preference pref = rootPreferences.getPreference(0);
196 rootPreferences.removeAll();
197
198 PreferenceScreen screen = getPreferenceScreen();
199 pref.setOrder(screen.getPreferenceCount());
200 screen.addPreference(pref);
201
202 return pref;
203 }
204
Jason Monkb37e2882016-01-11 14:27:20 -0500205 @Override
206 public void onResume() {
207 super.onResume();
208 updateState();
209 }
210
Fan Zhangcbc42182017-10-11 14:57:22 -0700211 @VisibleForTesting
212 static CharSequence formatUsage(Context context, String template, long usageLevel) {
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700213 final float LARGER_SIZE = 1.25f * 1.25f; // (1/0.8)^2
214 final float SMALLER_SIZE = 1.0f / LARGER_SIZE; // 0.8^2
Jan Nordqvist8a241f62018-03-07 09:59:06 -0800215 return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE);
216 }
217
218 static CharSequence formatUsage(Context context, String template, long usageLevel,
219 float larger, float smaller) {
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700220 final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
221
222 final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
Fan Zhang289677f2018-04-11 13:59:12 -0700223 usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS);
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700224 final SpannableString enlargedValue = new SpannableString(usedResult.value);
Jan Nordqvist8a241f62018-03-07 09:59:06 -0800225 enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS);
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700226
227 final SpannableString amountTemplate = new SpannableString(
228 context.getString(com.android.internal.R.string.fileSizeSuffix)
229 .replace("%1$s", "^1").replace("%2$s", "^2"));
230 final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
231 enlargedValue, usedResult.units);
232
233 final SpannableString fullTemplate = new SpannableString(template);
Jan Nordqvist8a241f62018-03-07 09:59:06 -0800234 fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS);
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700235 return TextUtils.expandTemplate(fullTemplate,
Fan Zhangcbc42182017-10-11 14:57:22 -0700236 BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
Roozbeh Pournader0761a4b2016-06-09 16:54:43 -0700237 }
238
Jason Monkb37e2882016-01-11 14:27:20 -0500239 private void updateState() {
Jason Monkb37e2882016-01-11 14:27:20 -0500240 PreferenceScreen screen = getPreferenceScreen();
241 for (int i = 1; i < screen.getPreferenceCount(); i++) {
Andreas Terzis446cd9d2018-03-17 16:27:20 -0700242 Preference currentPreference = screen.getPreference(i);
243 if (currentPreference instanceof TemplatePreferenceCategory) {
244 ((TemplatePreferenceCategory) currentPreference).pushTemplates(services);
245 }
Jason Monkb37e2882016-01-11 14:27:20 -0500246 }
247 }
248
249 @Override
Fan Zhang65076132016-08-08 10:25:13 -0700250 public int getMetricsCategory() {
Fan Zhang31b21002019-01-16 13:49:47 -0800251 return SettingsEnums.DATA_USAGE_SUMMARY;
Jason Monkb37e2882016-01-11 14:27:20 -0500252 }
253
Matthew Fritze9fad2892016-08-12 09:37:43 -0700254 @Override
255 public NetworkPolicyEditor getNetworkPolicyEditor() {
256 return services.mPolicyEditor;
257 }
258
259 @Override
260 public NetworkTemplate getNetworkTemplate() {
261 return mDefaultTemplate;
262 }
263
264 @Override
265 public void updateDataUsage() {
266 updateState();
Jan Nordqvist4fbe0f82018-01-22 14:43:43 -0800267 mSummaryController.updateState(mSummaryPreference);
Matthew Fritze9fad2892016-08-12 09:37:43 -0700268 }
Jason Monkb37e2882016-01-11 14:27:20 -0500269}