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