1b37e2887d3112082589997f1bbd802ec282cca6dJason Monk/* 2b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * Copyright (C) 2016 The Android Open Source Project 3b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * 4b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * except in compliance with the License. You may obtain a copy of the License at 6b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * 7b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * http://www.apache.org/licenses/LICENSE-2.0 8b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * 9b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * Unless required by applicable law or agreed to in writing, software distributed under the 10b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * KIND, either express or implied. See the License for the specific language governing 12b37e2887d3112082589997f1bbd802ec282cca6dJason Monk * permissions and limitations under the License. 13b37e2887d3112082589997f1bbd802ec282cca6dJason Monk */ 14b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 15b37e2887d3112082589997f1bbd802ec282cca6dJason Monkpackage com.android.settings.datausage; 16b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 17b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.content.Context; 18b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.net.NetworkPolicy; 19b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.net.NetworkStatsHistory; 209dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.net.TrafficStats; 21b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.support.v7.preference.Preference; 22b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.support.v7.preference.PreferenceViewHolder; 239dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.text.SpannableStringBuilder; 249dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.text.TextUtils; 259dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.text.format.Formatter; 269dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.text.style.ForegroundColorSpan; 27b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport android.util.AttributeSet; 289dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport android.util.SparseIntArray; 29b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport com.android.settings.R; 309dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monkimport com.android.settings.Utils; 31c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport com.android.settings.graph.UsageView; 32b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 33b37e2887d3112082589997f1bbd802ec282cca6dJason Monkpublic class ChartDataUsagePreference extends Preference { 34b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 359dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk // The resolution we show on the graph so that we can squash things down to ints. 369dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk // Set to half a meg for now. 379dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private static final long RESOLUTION = TrafficStats.MB_IN_BYTES / 2; 389dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 399dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private final int mWarningColor; 409dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private final int mLimitColor; 419dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 42b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private NetworkPolicy mPolicy; 43b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private long mStart; 44b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private long mEnd; 45b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private NetworkStatsHistory mNetwork; 46b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private int mSecondaryColor; 47b37e2887d3112082589997f1bbd802ec282cca6dJason Monk private int mSeriesColor; 48b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 49b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public ChartDataUsagePreference(Context context, AttributeSet attrs) { 50b37e2887d3112082589997f1bbd802ec282cca6dJason Monk super(context, attrs); 519dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk setSelectable(false); 526fca98f9051185c0e2cd6811825b84b49bd3d61bJason Monk mLimitColor = Utils.getColorAttr(context, android.R.attr.colorError); 536fca98f9051185c0e2cd6811825b84b49bd3d61bJason Monk mWarningColor = Utils.getColorAttr(context, android.R.attr.textColorSecondary); 549dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk setLayoutResource(R.layout.data_usage_graph); 55b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 56b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 57b37e2887d3112082589997f1bbd802ec282cca6dJason Monk @Override 58b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public void onBindViewHolder(PreferenceViewHolder holder) { 59b37e2887d3112082589997f1bbd802ec282cca6dJason Monk super.onBindViewHolder(holder); 609dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk UsageView chart = (UsageView) holder.findViewById(R.id.data_usage); 619dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (mNetwork == null) return; 629dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 639dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk int top = getTop(); 649dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.clearPaths(); 65c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza chart.configureGraph(toInt(mEnd - mStart), top); 669dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk calcPoints(chart); 679dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.setBottomLabels(new CharSequence[] { 689dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk Utils.formatDateRange(getContext(), mStart, mStart), 699dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk Utils.formatDateRange(getContext(), mEnd, mEnd), 709dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk }); 719dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 729dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk bindNetworkPolicy(chart, mPolicy, top); 739dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 749dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 759dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk public int getTop() { 769dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk NetworkStatsHistory.Entry entry = null; 779dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk long totalData = 0; 789dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final int start = mNetwork.getIndexBefore(mStart); 799dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final int end = mNetwork.getIndexAfter(mEnd); 809dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 819dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk for (int i = start; i <= end; i++) { 829dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk entry = mNetwork.getValues(i, entry); 839dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 849dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk // increment by current bucket total 859dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk totalData += entry.rxBytes + entry.txBytes; 869dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 879dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk long policyMax = mPolicy != null ? Math.max(mPolicy.limitBytes, mPolicy.warningBytes) : 0; 889dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk return (int) (Math.max(totalData, policyMax) / RESOLUTION); 899dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 909dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 919dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private void calcPoints(UsageView chart) { 929dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk SparseIntArray points = new SparseIntArray(); 939dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk NetworkStatsHistory.Entry entry = null; 949dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 959dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk long totalData = 0; 969dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 979dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final int start = mNetwork.getIndexAfter(mStart); 989dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final int end = mNetwork.getIndexAfter(mEnd); 999dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (start < 0) return; 1009dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1019dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk points.put(0, 0); 1029dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk for (int i = start; i <= end; i++) { 1039dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk entry = mNetwork.getValues(i, entry); 1049dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1059dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final long startTime = entry.bucketStart; 1069dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk final long endTime = startTime + entry.bucketDuration; 1079dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1089dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk // increment by current bucket total 1099dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk totalData += entry.rxBytes + entry.txBytes; 1109dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1115ce4345dff79bad525207d16ff3a05c08159530dJason Monk points.put(toInt(startTime - mStart + 1), (int) (totalData / RESOLUTION)); 1125ce4345dff79bad525207d16ff3a05c08159530dJason Monk points.put(toInt(endTime - mStart), (int) (totalData / RESOLUTION)); 1139dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1149dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (points.size() > 1) { 1159dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.addPath(points); 1169dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1179dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1189dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1195ce4345dff79bad525207d16ff3a05c08159530dJason Monk private int toInt(long l) { 1205ce4345dff79bad525207d16ff3a05c08159530dJason Monk // Don't need that much resolution on these times. 1215ce4345dff79bad525207d16ff3a05c08159530dJason Monk return (int) (l / (1000 * 60)); 1225ce4345dff79bad525207d16ff3a05c08159530dJason Monk } 1235ce4345dff79bad525207d16ff3a05c08159530dJason Monk 1249dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private void bindNetworkPolicy(UsageView chart, NetworkPolicy policy, int top) { 1259dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk CharSequence[] labels = new CharSequence[3]; 1269dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk int middleVisibility = 0; 1279dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk int topVisibility = 0; 1289dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (policy == null) { 1299dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk return; 1309dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1319dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1329dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { 1339dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk topVisibility = mLimitColor; 1349dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk labels[2] = getLabel(policy.limitBytes, R.string.data_usage_sweep_limit, mLimitColor); 1359dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1369dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1379dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { 1389dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.setDividerLoc((int) (policy.warningBytes / RESOLUTION)); 1399dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk float weight = policy.warningBytes / RESOLUTION / (float) top; 1409dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk float above = 1 - weight; 1419dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.setSideLabelWeights(above, weight); 1429dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk middleVisibility = mWarningColor; 1439dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk labels[1] = getLabel(policy.warningBytes, R.string.data_usage_sweep_warning, 1449dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk mWarningColor); 1459dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1469dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1479dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.setSideLabels(labels); 1489dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk chart.setDividerColors(middleVisibility, topVisibility); 1499dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk } 1509dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk 1519dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk private CharSequence getLabel(long bytes, int str, int mLimitColor) { 1529dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk Formatter.BytesResult result = Formatter.formatBytes(getContext().getResources(), 1539dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk bytes, Formatter.FLAG_SHORTER); 1549dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk CharSequence label = TextUtils.expandTemplate(getContext().getText(str), 1559dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk result.value, result.units); 1569dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk return new SpannableStringBuilder().append(label, new ForegroundColorSpan(mLimitColor), 0); 157b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 158b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 1599dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk public void setNetworkPolicy(NetworkPolicy policy) { 160b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mPolicy = policy; 161b37e2887d3112082589997f1bbd802ec282cca6dJason Monk notifyChanged(); 162b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 163b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 164b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public void setVisibleRange(long start, long end) { 165b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mStart = start; 166b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mEnd = end; 167b37e2887d3112082589997f1bbd802ec282cca6dJason Monk notifyChanged(); 168b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 169b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 170b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public long getInspectStart() { 171b37e2887d3112082589997f1bbd802ec282cca6dJason Monk return mStart; 172b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 173b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 174b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public long getInspectEnd() { 175b37e2887d3112082589997f1bbd802ec282cca6dJason Monk return mEnd; 176b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 177b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 1789dc9a0ebb383df0225c4d0f92cb837ccabe40021Jason Monk public void setNetworkStats(NetworkStatsHistory network) { 179b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mNetwork = network; 180b37e2887d3112082589997f1bbd802ec282cca6dJason Monk notifyChanged(); 181b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 182b37e2887d3112082589997f1bbd802ec282cca6dJason Monk 183b37e2887d3112082589997f1bbd802ec282cca6dJason Monk public void setColors(int seriesColor, int secondaryColor) { 184b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mSeriesColor = seriesColor; 185b37e2887d3112082589997f1bbd802ec282cca6dJason Monk mSecondaryColor = secondaryColor; 186b37e2887d3112082589997f1bbd802ec282cca6dJason Monk notifyChanged(); 187b37e2887d3112082589997f1bbd802ec282cca6dJason Monk } 188b37e2887d3112082589997f1bbd802ec282cca6dJason Monk} 189