• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
15 package com.android.settings.datausage;
16 
17 import android.app.Activity;
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.net.NetworkTemplate;
22 import android.os.Bundle;
23 import android.provider.SearchIndexableResource;
24 import android.support.annotation.VisibleForTesting;
25 import android.support.v7.preference.Preference;
26 import android.support.v7.preference.PreferenceScreen;
27 import android.telephony.SubscriptionInfo;
28 import android.telephony.SubscriptionManager;
29 import android.telephony.SubscriptionPlan;
30 import android.text.BidiFormatter;
31 import android.text.Spannable;
32 import android.text.SpannableString;
33 import android.text.TextUtils;
34 import android.text.format.Formatter;
35 import android.text.style.RelativeSizeSpan;
36 import android.view.MenuItem;
37 
38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
39 import com.android.settings.R;
40 import com.android.settings.Utils;
41 import com.android.settings.dashboard.SummaryLoader;
42 import com.android.settings.search.BaseSearchIndexProvider;
43 import com.android.settings.search.Indexable;
44 import com.android.settingslib.NetworkPolicyEditor;
45 import com.android.settingslib.core.AbstractPreferenceController;
46 import com.android.settingslib.net.DataUsageController;
47 
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 /**
52  * Settings preference fragment that displays data usage summary.
53  */
54 public class DataUsageSummary extends DataUsageBaseFragment implements Indexable,
55         DataUsageEditController {
56 
57     private static final String TAG = "DataUsageSummary";
58 
59     static final boolean LOGD = false;
60 
61     public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
62 
63     private static final String KEY_STATUS_HEADER = "status_header";
64 
65     // Mobile data keys
66     public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
67     public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable";
68     public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage";
69     public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference";
70 
71     // Wifi keys
72     public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
73     public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
74 
75     private DataUsageSummaryPreference mSummaryPreference;
76     private DataUsageSummaryPreferenceController mSummaryController;
77     private NetworkTemplate mDefaultTemplate;
78 
79     @Override
getHelpResource()80     public int getHelpResource() {
81         return R.string.help_url_data_usage;
82     }
83 
84     @Override
onCreate(Bundle icicle)85     public void onCreate(Bundle icicle) {
86         super.onCreate(icicle);
87         Context context = getContext();
88 
89         boolean hasMobileData = DataUsageUtils.hasMobileData(context);
90 
91         int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
92         if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
93             hasMobileData = false;
94         }
95         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
96         mSummaryPreference = (DataUsageSummaryPreference) findPreference(KEY_STATUS_HEADER);
97 
98         if (!hasMobileData || !isAdmin()) {
99             removePreference(KEY_RESTRICT_BACKGROUND);
100         }
101         boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
102         if (hasMobileData) {
103             addMobileSection(defaultSubId);
104             if (DataUsageUtils.hasSim(context) && hasWifiRadio) {
105                 // If the device has a SIM installed, the data usage section shows usage for mobile,
106                 // and the WiFi section is added if there is a WiFi radio - legacy behavior.
107                 addWifiSection();
108             }
109             // Do not add the WiFi section if either there is no WiFi radio (obviously) or if no
110             // SIM is installed. In the latter case the data usage section will show WiFi usage and
111             // there should be no explicit WiFi section added.
112         } else if (hasWifiRadio) {
113             addWifiSection();
114         }
115         if (DataUsageUtils.hasEthernet(context)) {
116             addEthernetSection();
117         }
118         setHasOptionsMenu(true);
119     }
120 
121     @Override
onOptionsItemSelected(MenuItem item)122     public boolean onOptionsItemSelected(MenuItem item) {
123         switch (item.getItemId()) {
124             case R.id.data_usage_menu_cellular_networks: {
125                 final Intent intent = new Intent(Intent.ACTION_MAIN);
126                 intent.setComponent(new ComponentName("com.android.phone",
127                         "com.android.phone.MobileNetworkSettings"));
128                 startActivity(intent);
129                 return true;
130             }
131         }
132         return false;
133     }
134 
135     @Override
onPreferenceTreeClick(Preference preference)136     public boolean onPreferenceTreeClick(Preference preference) {
137         if (preference == findPreference(KEY_STATUS_HEADER)) {
138             BillingCycleSettings.BytesEditorFragment.show(this, false);
139             return false;
140         }
141         return super.onPreferenceTreeClick(preference);
142     }
143 
144     @Override
getPreferenceScreenResId()145     protected int getPreferenceScreenResId() {
146         return R.xml.data_usage;
147     }
148 
149     @Override
getLogTag()150     protected String getLogTag() {
151         return TAG;
152     }
153 
154     @Override
createPreferenceControllers(Context context)155     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
156         final Activity activity = getActivity();
157         final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
158         mSummaryController =
159                 new DataUsageSummaryPreferenceController(activity, getLifecycle(), this);
160         controllers.add(mSummaryController);
161         getLifecycle().addObserver(mSummaryController);
162         return controllers;
163     }
164 
165     @VisibleForTesting
addMobileSection(int subId)166     void addMobileSection(int subId) {
167         addMobileSection(subId, null);
168     }
169 
addMobileSection(int subId, SubscriptionInfo subInfo)170     private void addMobileSection(int subId, SubscriptionInfo subInfo) {
171         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
172                 inflatePreferences(R.xml.data_usage_cellular);
173         category.setTemplate(getNetworkTemplate(subId), subId, services);
174         category.pushTemplates(services);
175         if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) {
176             Preference title  = category.findPreference(KEY_MOBILE_USAGE_TITLE);
177             title.setTitle(subInfo.getDisplayName());
178         }
179     }
180 
181     @VisibleForTesting
addWifiSection()182     void addWifiSection() {
183         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
184                 inflatePreferences(R.xml.data_usage_wifi);
185         category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services);
186     }
187 
addEthernetSection()188     private void addEthernetSection() {
189         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
190                 inflatePreferences(R.xml.data_usage_ethernet);
191         category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
192     }
193 
inflatePreferences(int resId)194     private Preference inflatePreferences(int resId) {
195         PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
196                 getPrefContext(), resId, null);
197         Preference pref = rootPreferences.getPreference(0);
198         rootPreferences.removeAll();
199 
200         PreferenceScreen screen = getPreferenceScreen();
201         pref.setOrder(screen.getPreferenceCount());
202         screen.addPreference(pref);
203 
204         return pref;
205     }
206 
getNetworkTemplate(int subscriptionId)207     private NetworkTemplate getNetworkTemplate(int subscriptionId) {
208         NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
209                 services.mTelephonyManager.getSubscriberId(subscriptionId));
210         return NetworkTemplate.normalize(mobileAll,
211                 services.mTelephonyManager.getMergedSubscriberIds());
212     }
213 
214     @Override
onResume()215     public void onResume() {
216         super.onResume();
217         updateState();
218     }
219 
220     @VisibleForTesting
formatUsage(Context context, String template, long usageLevel)221     static CharSequence formatUsage(Context context, String template, long usageLevel) {
222         final float LARGER_SIZE = 1.25f * 1.25f;  // (1/0.8)^2
223         final float SMALLER_SIZE = 1.0f / LARGER_SIZE;  // 0.8^2
224         return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE);
225     }
226 
formatUsage(Context context, String template, long usageLevel, float larger, float smaller)227     static CharSequence formatUsage(Context context, String template, long usageLevel,
228                                     float larger, float smaller) {
229         final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
230 
231         final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
232                 usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS);
233         final SpannableString enlargedValue = new SpannableString(usedResult.value);
234         enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS);
235 
236         final SpannableString amountTemplate = new SpannableString(
237                 context.getString(com.android.internal.R.string.fileSizeSuffix)
238                 .replace("%1$s", "^1").replace("%2$s", "^2"));
239         final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
240                 enlargedValue, usedResult.units);
241 
242         final SpannableString fullTemplate = new SpannableString(template);
243         fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS);
244         return TextUtils.expandTemplate(fullTemplate,
245                 BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
246     }
247 
updateState()248     private void updateState() {
249         PreferenceScreen screen = getPreferenceScreen();
250         for (int i = 1; i < screen.getPreferenceCount(); i++) {
251           Preference currentPreference = screen.getPreference(i);
252           if (currentPreference instanceof TemplatePreferenceCategory) {
253             ((TemplatePreferenceCategory) currentPreference).pushTemplates(services);
254           }
255         }
256     }
257 
258     @Override
getMetricsCategory()259     public int getMetricsCategory() {
260         return MetricsEvent.DATA_USAGE_SUMMARY;
261     }
262 
263     @Override
getNetworkPolicyEditor()264     public NetworkPolicyEditor getNetworkPolicyEditor() {
265         return services.mPolicyEditor;
266     }
267 
268     @Override
getNetworkTemplate()269     public NetworkTemplate getNetworkTemplate() {
270         return mDefaultTemplate;
271     }
272 
273     @Override
updateDataUsage()274     public void updateDataUsage() {
275         updateState();
276         mSummaryController.updateState(mSummaryPreference);
277     }
278 
279     private static class SummaryProvider
280             implements SummaryLoader.SummaryProvider {
281 
282         private final Activity mActivity;
283         private final SummaryLoader mSummaryLoader;
284         private final DataUsageController mDataController;
285 
SummaryProvider(Activity activity, SummaryLoader summaryLoader)286         public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
287             mActivity = activity;
288             mSummaryLoader = summaryLoader;
289             mDataController = new DataUsageController(activity);
290         }
291 
292         @Override
setListening(boolean listening)293         public void setListening(boolean listening) {
294             if (listening) {
295                 if (DataUsageUtils.hasSim(mActivity)) {
296                     mSummaryLoader.setSummary(this,
297                             mActivity.getString(R.string.data_usage_summary_format,
298                                     formatUsedData()));
299                 } else {
300                     final DataUsageController.DataUsageInfo info =
301                             mDataController
302                                     .getDataUsageInfo(NetworkTemplate.buildTemplateWifiWildcard());
303 
304                     if (info == null) {
305                         mSummaryLoader.setSummary(this, null);
306                     } else {
307                         final CharSequence wifiFormat = mActivity
308                                 .getText(R.string.data_usage_wifi_format);
309                         final CharSequence sizeText =
310                                 DataUsageUtils.formatDataUsage(mActivity, info.usageLevel);
311                         mSummaryLoader.setSummary(this,
312                                 TextUtils.expandTemplate(wifiFormat, sizeText));
313                     }
314                 }
315             }
316         }
317 
formatUsedData()318         private CharSequence formatUsedData() {
319             SubscriptionManager subscriptionManager = (SubscriptionManager) mActivity
320                 .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
321             int defaultSubId = subscriptionManager.getDefaultSubscriptionId();
322             if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
323                 return formatFallbackData();
324             }
325             SubscriptionPlan dfltPlan = DataUsageSummaryPreferenceController
326                     .getPrimaryPlan(subscriptionManager, defaultSubId);
327             if (dfltPlan == null) {
328                 return formatFallbackData();
329             }
330             if (DataUsageSummaryPreferenceController.unlimited(dfltPlan.getDataLimitBytes())) {
331                 return DataUsageUtils.formatDataUsage(mActivity, dfltPlan.getDataUsageBytes());
332             } else {
333                 return Utils.formatPercentage(dfltPlan.getDataUsageBytes(),
334                     dfltPlan.getDataLimitBytes());
335             }
336         }
337 
formatFallbackData()338         private CharSequence formatFallbackData() {
339             DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
340             if (info == null) {
341                 return DataUsageUtils.formatDataUsage(mActivity, 0);
342             } else if (info.limitLevel <= 0) {
343                 return DataUsageUtils.formatDataUsage(mActivity, info.usageLevel);
344             } else {
345                 return Utils.formatPercentage(info.usageLevel, info.limitLevel);
346             }
347         }
348     }
349 
350     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
351         = SummaryProvider::new;
352 
353     /**
354      * For search
355      */
356     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
357         new BaseSearchIndexProvider() {
358 
359             @Override
360             public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
361                     boolean enabled) {
362                 List<SearchIndexableResource> resources = new ArrayList<>();
363                 SearchIndexableResource resource = new SearchIndexableResource(context);
364                 resource.xmlResId = R.xml.data_usage;
365                 resources.add(resource);
366 
367                 resource = new SearchIndexableResource(context);
368                 resource.xmlResId = R.xml.data_usage_cellular;
369                 resources.add(resource);
370 
371                 resource = new SearchIndexableResource(context);
372                 resource.xmlResId = R.xml.data_usage_wifi;
373                 resources.add(resource);
374 
375                 return resources;
376             }
377 
378             @Override
379             public List<String> getNonIndexableKeys(Context context) {
380                 List<String> keys = super.getNonIndexableKeys(context);
381 
382                 if (!DataUsageUtils.hasMobileData(context)) {
383                     keys.add(KEY_MOBILE_USAGE_TITLE);
384                     keys.add(KEY_MOBILE_DATA_USAGE_TOGGLE);
385                     keys.add(KEY_MOBILE_DATA_USAGE);
386                     keys.add(KEY_MOBILE_BILLING_CYCLE);
387                 }
388 
389                 if (!DataUsageUtils.hasWifiRadio(context)) {
390                     keys.add(KEY_WIFI_DATA_USAGE);
391                 }
392 
393                 // This title is named Wifi, and will confuse users.
394                 keys.add(KEY_WIFI_USAGE_TITLE);
395 
396                 return keys;
397             }
398         };
399 }
400