/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.settings.datausage; import android.content.Context; import android.content.res.TypedArray; import android.net.NetworkPolicy; import android.net.NetworkStatsHistory; import android.net.TrafficStats; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceViewHolder; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.format.Formatter; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; import android.util.SparseIntArray; import com.android.settings.R; import com.android.settings.Utils; import com.android.settingslib.graph.UsageView; public class ChartDataUsagePreference extends Preference { // The resolution we show on the graph so that we can squash things down to ints. // Set to half a meg for now. private static final long RESOLUTION = TrafficStats.MB_IN_BYTES / 2; private final int mWarningColor; private final int mLimitColor; private NetworkPolicy mPolicy; private long mStart; private long mEnd; private NetworkStatsHistory mNetwork; private int mSecondaryColor; private int mSeriesColor; public ChartDataUsagePreference(Context context, AttributeSet attrs) { super(context, attrs); setSelectable(false); // TODO: Resource. mLimitColor = 0xfff4511e; TypedArray a = context.getTheme().obtainStyledAttributes( new int[]{android.R.attr.textColorSecondary}); mWarningColor = a.getColor(0, 0); setLayoutResource(R.layout.data_usage_graph); } @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); UsageView chart = (UsageView) holder.findViewById(R.id.data_usage); if (mNetwork == null) return; int top = getTop(); chart.clearPaths(); chart.configureGraph(toInt(mEnd - mStart), top, false, false); calcPoints(chart); chart.setBottomLabels(new CharSequence[] { Utils.formatDateRange(getContext(), mStart, mStart), Utils.formatDateRange(getContext(), mEnd, mEnd), }); bindNetworkPolicy(chart, mPolicy, top); } public int getTop() { NetworkStatsHistory.Entry entry = null; long totalData = 0; final int start = mNetwork.getIndexBefore(mStart); final int end = mNetwork.getIndexAfter(mEnd); for (int i = start; i <= end; i++) { entry = mNetwork.getValues(i, entry); // increment by current bucket total totalData += entry.rxBytes + entry.txBytes; } long policyMax = mPolicy != null ? Math.max(mPolicy.limitBytes, mPolicy.warningBytes) : 0; return (int) (Math.max(totalData, policyMax) / RESOLUTION); } private void calcPoints(UsageView chart) { SparseIntArray points = new SparseIntArray(); NetworkStatsHistory.Entry entry = null; long totalData = 0; final int start = mNetwork.getIndexAfter(mStart); final int end = mNetwork.getIndexAfter(mEnd); if (start < 0) return; points.put(0, 0); for (int i = start; i <= end; i++) { entry = mNetwork.getValues(i, entry); final long startTime = entry.bucketStart; final long endTime = startTime + entry.bucketDuration; // increment by current bucket total totalData += entry.rxBytes + entry.txBytes; points.put(toInt(startTime - mStart + 1), (int) (totalData / RESOLUTION)); points.put(toInt(endTime - mStart), (int) (totalData / RESOLUTION)); } if (points.size() > 1) { chart.addPath(points); } } private int toInt(long l) { // Don't need that much resolution on these times. return (int) (l / (1000 * 60)); } private void bindNetworkPolicy(UsageView chart, NetworkPolicy policy, int top) { CharSequence[] labels = new CharSequence[3]; int middleVisibility = 0; int topVisibility = 0; if (policy == null) { return; } if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { topVisibility = mLimitColor; labels[2] = getLabel(policy.limitBytes, R.string.data_usage_sweep_limit, mLimitColor); } if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { chart.setDividerLoc((int) (policy.warningBytes / RESOLUTION)); float weight = policy.warningBytes / RESOLUTION / (float) top; float above = 1 - weight; chart.setSideLabelWeights(above, weight); middleVisibility = mWarningColor; labels[1] = getLabel(policy.warningBytes, R.string.data_usage_sweep_warning, mWarningColor); } chart.setSideLabels(labels); chart.setDividerColors(middleVisibility, topVisibility); } private CharSequence getLabel(long bytes, int str, int mLimitColor) { Formatter.BytesResult result = Formatter.formatBytes(getContext().getResources(), bytes, Formatter.FLAG_SHORTER); CharSequence label = TextUtils.expandTemplate(getContext().getText(str), result.value, result.units); return new SpannableStringBuilder().append(label, new ForegroundColorSpan(mLimitColor), 0); } public void setNetworkPolicy(NetworkPolicy policy) { mPolicy = policy; notifyChanged(); } public void setVisibleRange(long start, long end) { mStart = start; mEnd = end; notifyChanged(); } public long getInspectStart() { return mStart; } public long getInspectEnd() { return mEnd; } public void setNetworkStats(NetworkStatsHistory network) { mNetwork = network; notifyChanged(); } public void setColors(int seriesColor, int secondaryColor) { mSeriesColor = seriesColor; mSecondaryColor = secondaryColor; notifyChanged(); } }