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