OSDN Git Service

Merge "Change text for data warning and data limit." into pi-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / datausage / DataUsageSummaryPreferenceController.java
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings.datausage;
18
19 import android.content.Context;
20 import android.content.Intent;
21 import android.net.NetworkPolicyManager;
22 import android.net.NetworkTemplate;
23 import android.support.annotation.VisibleForTesting;
24 import android.support.v7.preference.Preference;
25 import android.telephony.SubscriptionInfo;
26 import android.telephony.SubscriptionManager;
27 import android.telephony.SubscriptionPlan;
28 import android.text.BidiFormatter;
29 import android.text.Spannable;
30 import android.text.SpannableString;
31 import android.text.TextUtils;
32 import android.text.format.Formatter;
33 import android.text.style.RelativeSizeSpan;
34 import android.util.Log;
35 import android.util.RecurrenceRule;
36
37 import com.android.internal.util.CollectionUtils;
38 import com.android.settings.R;
39 import com.android.settings.core.BasePreferenceController;
40 import com.android.settings.core.FeatureFlags;
41 import com.android.settingslib.NetworkPolicyEditor;
42 import com.android.settingslib.net.DataUsageController;
43
44 import java.util.List;
45
46 /**
47  * This is the controller for the top of the data usage screen that retrieves carrier data from the
48  * new subscriptions framework API if available. The controller reads subscription information from
49  * the framework and falls back to legacy usage data if none are available.
50  */
51 public class DataUsageSummaryPreferenceController extends BasePreferenceController {
52
53     private static final String TAG = "DataUsageController";
54     private static final String KEY = "status_header";
55     private static final long PETA = 1000000000000000L;
56     private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f;  // (1/0.8)^2
57     private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE;  // 0.8^2
58
59     private final DataUsageController mDataUsageController;
60     private final DataUsageInfoController mDataInfoController;
61     private final NetworkTemplate mDefaultTemplate;
62     private final NetworkPolicyEditor mPolicyEditor;
63     private final int mDataUsageTemplate;
64     private final boolean mHasMobileData;
65     private final SubscriptionManager mSubscriptionManager;
66
67     /** Name of the carrier, or null if not available */
68     private CharSequence mCarrierName;
69
70     /** The number of registered plans, [0,N] */
71     private int mDataplanCount;
72
73     /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
74     private long mSnapshotTime;
75
76     /**
77      * The size of the first registered plan if one exists or the size of the warning if it is set.
78      * -1 if no information is available.
79      */
80     private long mDataplanSize;
81     /** The number of bytes used since the start of the cycle. */
82     private long mDataplanUse;
83     /** The starting time of the billing cycle in ms since the epoch */
84     private long mCycleStart;
85     /** The ending time of the billing cycle in ms since the epoch */
86     private long mCycleEnd;
87
88     private Intent mManageSubscriptionIntent;
89
90     public DataUsageSummaryPreferenceController(Context context) {
91         super(context, KEY);
92
93         final int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
94         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
95         NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
96         mPolicyEditor = new NetworkPolicyEditor(policyManager);
97
98         mHasMobileData = DataUsageUtils.hasMobileData(context)
99                 && defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
100
101         mDataUsageController = new DataUsageController(context);
102         mDataInfoController = new DataUsageInfoController();
103
104         if (mHasMobileData) {
105             mDataUsageTemplate = R.string.cell_data_template;
106         } else if (DataUsageUtils.hasWifiRadio(context)) {
107             mDataUsageTemplate = R.string.wifi_data_template;
108         } else {
109             mDataUsageTemplate = R.string.ethernet_data_template;
110         }
111
112         mSubscriptionManager = (SubscriptionManager)
113                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
114     }
115
116     @VisibleForTesting
117     DataUsageSummaryPreferenceController(
118             Context context,
119             DataUsageController dataUsageController,
120             DataUsageInfoController dataInfoController,
121             NetworkTemplate defaultTemplate,
122             NetworkPolicyEditor policyEditor,
123             int dataUsageTemplate,
124             boolean hasMobileData,
125             SubscriptionManager subscriptionManager) {
126         super(context, KEY);
127         mDataUsageController = dataUsageController;
128         mDataInfoController = dataInfoController;
129         mDefaultTemplate = defaultTemplate;
130         mPolicyEditor = policyEditor;
131         mDataUsageTemplate = dataUsageTemplate;
132         mHasMobileData = hasMobileData;
133         mSubscriptionManager = subscriptionManager;
134     }
135
136     @VisibleForTesting
137     void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) {
138         mDataplanCount = dataPlanCount;
139         mDataplanSize = dataPlanSize;
140         mDataplanUse = dataPlanUse;
141     }
142
143     @VisibleForTesting
144     void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) {
145         mCarrierName = carrierName;
146         mSnapshotTime = snapshotTime;
147         mCycleEnd = cycleEnd;
148         mManageSubscriptionIntent = intent;
149     }
150
151     @Override
152     public int getAvailabilityStatus() {
153         return AVAILABLE;
154     }
155
156     @Override
157     public void updateState(Preference preference) {
158         DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
159         DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
160                 mDefaultTemplate);
161
162         mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
163
164         if (mSubscriptionManager != null) {
165             refreshDataplanInfo(info);
166         }
167
168         if (info.warningLevel > 0 && info.limitLevel > 0) {
169                 summaryPreference.setLimitInfo(TextUtils.expandTemplate(
170                         mContext.getText(R.string.cell_data_warning_and_limit),
171                         Formatter.formatFileSize(mContext, info.warningLevel),
172                         Formatter.formatFileSize(mContext, info.limitLevel)).toString());
173         } else if (info.warningLevel > 0) {
174                 summaryPreference.setLimitInfo(TextUtils.expandTemplate(
175                         mContext.getText(R.string.cell_data_warning),
176                         Formatter.formatFileSize(mContext, info.warningLevel)).toString());
177         } else if (info.limitLevel > 0) {
178             summaryPreference.setLimitInfo(TextUtils.expandTemplate(
179                     mContext.getText(R.string.cell_data_limit),
180                     Formatter.formatFileSize(mContext, info.limitLevel)).toString());
181         } else {
182             summaryPreference.setLimitInfo(null);
183         }
184
185         summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData);
186
187         if (mDataplanSize <= 0) {
188             summaryPreference.setChartEnabled(false);
189         } else {
190             summaryPreference.setChartEnabled(true);
191             summaryPreference.setLabels(Formatter.formatFileSize(mContext, 0 /* sizeBytes */),
192                     Formatter.formatFileSize(mContext, mDataplanSize));
193             summaryPreference.setProgress(mDataplanUse / (float) mDataplanSize);
194         }
195         summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName,
196                 mDataplanCount, mManageSubscriptionIntent);
197     }
198
199     private String getLimitText(long limit, int textId) {
200         if (limit <= 0) {
201             return null;
202         }
203         return mContext.getString(textId, Formatter.formatFileSize(mContext, limit));
204     }
205
206     // TODO(b/70950124) add test for this method once the robolectric shadow run script is
207     // completed (b/3526807)
208     private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) {
209         // reset data before overwriting
210         mCarrierName = null;
211         mDataplanCount = 0;
212         mDataplanSize = mDataInfoController.getSummaryLimit(info);
213         mDataplanUse = info.usageLevel;
214         mCycleStart = info.cycleStart;
215         mCycleEnd = info.cycleEnd;
216         mSnapshotTime = -1L;
217
218         final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
219         final SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
220         if (subInfo != null && mHasMobileData) {
221             mCarrierName = subInfo.getCarrierName();
222             List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(defaultSubId);
223             final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager, defaultSubId);
224             if (primaryPlan != null) {
225                 mDataplanCount = plans.size();
226                 mDataplanSize = primaryPlan.getDataLimitBytes();
227                 if (unlimited(mDataplanSize)) {
228                     mDataplanSize = 0L;
229                 }
230                 mDataplanUse = primaryPlan.getDataUsageBytes();
231
232                 RecurrenceRule rule = primaryPlan.getCycleRule();
233                 if (rule != null && rule.start != null && rule.end != null) {
234                     mCycleStart = rule.start.toEpochSecond() * 1000L;
235                     mCycleEnd = rule.end.toEpochSecond() * 1000L;
236                 }
237                 mSnapshotTime = primaryPlan.getDataUsageTime();
238             }
239         }
240         mManageSubscriptionIntent =
241                 mSubscriptionManager.createManageSubscriptionIntent(defaultSubId);
242         Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + defaultSubId
243                 + ", intent " + mManageSubscriptionIntent);
244     }
245
246     public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) {
247         List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(primaryId);
248         if (CollectionUtils.isEmpty(plans)) {
249             return null;
250         }
251         // First plan in the list is the primary plan
252         SubscriptionPlan plan = plans.get(0);
253         return plan.getDataLimitBytes() > 0
254                 && saneSize(plan.getDataUsageBytes())
255                 && plan.getCycleRule() != null ? plan : null;
256     }
257
258     private static boolean saneSize(long value) {
259         return value >= 0L && value < PETA;
260     }
261
262     public static boolean unlimited(long size) {
263         return size == SubscriptionPlan.BYTES_UNLIMITED;
264     }
265
266     @VisibleForTesting
267     private static CharSequence formatUsage(Context context, String template, long usageLevel) {
268         final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
269
270         final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
271                 usageLevel, Formatter.FLAG_CALCULATE_ROUNDED);
272         final SpannableString enlargedValue = new SpannableString(usedResult.value);
273         enlargedValue.setSpan(
274                 new RelativeSizeSpan(RELATIVE_SIZE_LARGE), 0, enlargedValue.length(), FLAGS);
275
276         final SpannableString amountTemplate = new SpannableString(
277                 context.getString(com.android.internal.R.string.fileSizeSuffix)
278                         .replace("%1$s", "^1").replace("%2$s", "^2"));
279         final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
280                 enlargedValue, usedResult.units);
281
282         final SpannableString fullTemplate = new SpannableString(template);
283         fullTemplate.setSpan(
284                 new RelativeSizeSpan(RELATIVE_SIZE_SMALL), 0, fullTemplate.length(), FLAGS);
285         return TextUtils.expandTemplate(fullTemplate,
286                 BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
287     }
288 }