1ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/*
2ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Copyright (C) 2011 The Android Open Source Project
3ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
4ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * you may not use this file except in compliance with the License.
6ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * You may obtain a copy of the License at
7ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
8ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
10ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * See the License for the specific language governing permissions and
14ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * limitations under the License.
15ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */
16ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
17ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypackage com.android.settings.widget;
18ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
192412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkeyimport static android.text.format.DateUtils.DAY_IN_MILLIS;
202412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkeyimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
212412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
22ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.content.Context;
2352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.content.res.TypedArray;
24ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Canvas;
25ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Color;
26e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkeyimport android.graphics.DashPathEffect;
27ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint;
28ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint.Style;
29ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Path;
30ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.RectF;
31ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.net.NetworkStatsHistory;
3252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.AttributeSet;
33ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.util.Log;
34ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View;
35ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
3652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport com.android.settings.R;
37ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport com.google.common.base.Preconditions;
38ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
39ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/**
40ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * {@link NetworkStatsHistory} series to render inside a {@link ChartView},
41ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * using {@link ChartAxis} to map into screen coordinates.
42ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */
43ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypublic class ChartNetworkSeriesView extends View {
44ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private static final String TAG = "ChartNetworkSeriesView";
45e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private static final boolean LOGD = false;
46ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
47a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey    private static final boolean ESTIMATE_ENABLED = false;
48a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey
4952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private ChartAxis mHoriz;
5052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private ChartAxis mVert;
51ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
524dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey    private Paint mPaintStroke;
534dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey    private Paint mPaintFill;
5452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private Paint mPaintFillSecondary;
552412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey    private Paint mPaintEstimate;
56ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
57ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private NetworkStatsHistory mStats;
58ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
59ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private Path mPathStroke;
60ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private Path mPathFill;
612412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey    private Path mPathEstimate;
62ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
63b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private long mStart;
64b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private long mEnd;
65b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
6652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private long mPrimaryLeft;
6752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private long mPrimaryRight;
68ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
69e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    /** Series will be extended to reach this end time. */
70e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private long mEndTime = Long.MIN_VALUE;
71e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
72b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private boolean mPathValid = false;
73e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private boolean mEstimateVisible = false;
74e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
75e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private long mMax;
76e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private long mMaxEstimate;
77e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
7852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartNetworkSeriesView(Context context) {
7952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        this(context, null, 0);
8052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
81ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
8252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartNetworkSeriesView(Context context, AttributeSet attrs) {
8352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        this(context, attrs, 0);
8452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
8552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
8652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) {
8752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        super(context, attrs, defStyle);
8852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
8952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final TypedArray a = context.obtainStyledAttributes(
9052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey                attrs, R.styleable.ChartNetworkSeriesView, defStyle, 0);
9152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
9252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final int stroke = a.getColor(R.styleable.ChartNetworkSeriesView_strokeColor, Color.RED);
9352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED);
9452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final int fillSecondary = a.getColor(
9552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey                R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED);
9652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
9752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        setChartColor(stroke, fill, fillSecondary);
98f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        setWillNotDraw(false);
99ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
10052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        a.recycle();
1014dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey
1024dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPathStroke = new Path();
1034dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPathFill = new Path();
1042412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPathEstimate = new Path();
1054dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey    }
1064dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey
10752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    void init(ChartAxis horiz, ChartAxis vert) {
10852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
10952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mVert = Preconditions.checkNotNull(vert, "missing vert");
11052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
11152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
11252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public void setChartColor(int stroke, int fill, int fillSecondary) {
113ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke = new Paint();
11454d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        mPaintStroke.setStrokeWidth(4.0f * getResources().getDisplayMetrics().density);
1154dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPaintStroke.setColor(stroke);
116ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke.setStyle(Style.STROKE);
117ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke.setAntiAlias(true);
118ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
119ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill = new Paint();
1204dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPaintFill.setColor(fill);
121ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill.setStyle(Style.FILL);
122ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill.setAntiAlias(true);
123ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
12452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary = new Paint();
12552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setColor(fillSecondary);
12652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setStyle(Style.FILL);
12752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setAntiAlias(true);
1282412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
1292412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate = new Paint();
1302412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setStrokeWidth(3.0f);
1312412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setColor(fillSecondary);
1322412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setStyle(Style.STROKE);
1332412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setAntiAlias(true);
134e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1));
135ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
136ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
137ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public void bindNetworkStats(NetworkStatsHistory stats) {
138ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mStats = stats;
139b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        invalidatePath();
140f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        invalidate();
141ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
142ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
143b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    public void setBounds(long start, long end) {
144b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mStart = start;
145b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mEnd = end;
146b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    }
147b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
148f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey    /**
149f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey     * Set the range to paint with {@link #mPaintFill}, leaving the remaining
150f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey     * area to be painted with {@link #mPaintFillSecondary}.
151f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey     */
15252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public void setPrimaryRange(long left, long right) {
15352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPrimaryLeft = left;
15452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPrimaryRight = right;
15552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        invalidate();
156ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
157ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
158b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    public void invalidatePath() {
159b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mPathValid = false;
160b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mMax = 0;
161b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        invalidate();
162ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
163ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
164ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    /**
165ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     * Erase any existing {@link Path} and generate series outline based on
166ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     * currently bound {@link NetworkStatsHistory} data.
167ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     */
168b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private void generatePath() {
1698a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        if (LOGD) Log.d(TAG, "generatePath()");
1708a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey
171e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mMax = 0;
172ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathStroke.reset();
173ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathFill.reset();
1742412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPathEstimate.reset();
175b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mPathValid = true;
176ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
177ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // bail when not enough stats to render
17854d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        if (mStats == null || mStats.size() < 2) {
17954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey            return;
18054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        }
181ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
182ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int width = getWidth();
183ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int height = getHeight();
184ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
185ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        boolean started = false;
186ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        float lastX = 0;
187f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        float lastY = height;
188f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        long lastTime = mHoriz.convertToValue(lastX);
189f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
190f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        // move into starting position
191f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathStroke.moveTo(lastX, lastY);
192f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathFill.moveTo(lastX, lastY);
193ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
1948a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        // TODO: count fractional data from first bucket crossing start;
1958a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        // currently it only accepts first full bucket.
1968a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey
197ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        long totalData = 0;
198ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
199ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey        NetworkStatsHistory.Entry entry = null;
200b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
201b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final int start = mStats.getIndexBefore(mStart);
202b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final int end = mStats.getIndexAfter(mEnd);
203b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        for (int i = start; i <= end; i++) {
204ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey            entry = mStats.getValues(i, entry);
205ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey
206f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final long startTime = entry.bucketStart;
207f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final long endTime = startTime + entry.bucketDuration;
208f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
209f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float startX = mHoriz.convertToPoint(startTime);
210f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float endX = mHoriz.convertToPoint(endTime);
211ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
212ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            // skip until we find first stats on screen
213f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            if (endX < 0) continue;
214f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
215f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            // increment by current bucket total
216f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            totalData += entry.rxBytes + entry.txBytes;
217ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
218f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float startY = lastY;
219f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float endY = mVert.convertToPoint(totalData);
220f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
221f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            if (lastTime != startTime) {
222f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                // gap in buckets; line to start of current bucket
223f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                mPathStroke.lineTo(startX, startY);
224f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                mPathFill.lineTo(startX, startY);
225ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
226ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
227f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            // always draw to end of current bucket
228f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathStroke.lineTo(endX, endY);
229f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathFill.lineTo(endX, endY);
230f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
231f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastX = endX;
232f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastY = endY;
233f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastTime = endTime;
234ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
235ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
236e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        // when data falls short, extend to requested end time
237e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        if (lastTime < mEndTime) {
238e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            lastX = mHoriz.convertToPoint(mEndTime);
239e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
240f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathStroke.lineTo(lastX, lastY);
241f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathFill.lineTo(lastX, lastY);
242e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        }
243e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
244ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        if (LOGD) {
245ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final RectF bounds = new RectF();
246ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            mPathFill.computeBounds(bounds, true);
2478e911d7b1a6817f67480b7677f8d36ab3bfb00f2Jeff Sharkey            Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData="
2488e911d7b1a6817f67480b7677f8d36ab3bfb00f2Jeff Sharkey                    + totalData);
249ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
250ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
251ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // drop to bottom of graph from current location
252ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathFill.lineTo(lastX, height);
253f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathFill.lineTo(0, height);
2542412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
255e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mMax = totalData;
256e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
257a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        if (ESTIMATE_ENABLED) {
258a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            // build estimated data
259a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            mPathEstimate.moveTo(lastX, lastY);
2602412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
261a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long now = System.currentTimeMillis();
262a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long bucketDuration = mStats.getBucketDuration();
2632412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
264a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            // long window is average over two weeks
265a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            entry = mStats.getValues(lastTime - WEEK_IN_MILLIS * 2, lastTime, now, entry);
266a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long longWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
267a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                    / entry.bucketDuration;
2682412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
269a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            long futureTime = 0;
270a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            while (lastX < width) {
271a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                futureTime += bucketDuration;
2722412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
273a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                // short window is day average last week
274a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                final long lastWeekTime = lastTime - WEEK_IN_MILLIS + (futureTime % WEEK_IN_MILLIS);
275a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                entry = mStats.getValues(lastWeekTime - DAY_IN_MILLIS, lastWeekTime, now, entry);
276a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                final long shortWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
277a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                        / entry.bucketDuration;
2782412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
279a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                totalData += (longWindow * 7 + shortWindow * 3) / 10;
2802412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
281a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                lastX = mHoriz.convertToPoint(lastTime + futureTime);
282a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                lastY = mVert.convertToPoint(totalData);
2832412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
284a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                mPathEstimate.lineTo(lastX, lastY);
285a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            }
286e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
287a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            mMaxEstimate = totalData;
288a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        }
28954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey
29054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        invalidate();
291e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
292e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
293e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public void setEndTime(long endTime) {
294e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mEndTime = endTime;
295e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
296e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
297e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public void setEstimateVisible(boolean estimateVisible) {
298a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        mEstimateVisible = ESTIMATE_ENABLED ? estimateVisible : false;
299e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        invalidate();
300e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
301e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
302e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public long getMaxEstimate() {
303e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        return mMaxEstimate;
304e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
305e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
306e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public long getMaxVisible() {
307b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final long maxVisible = mEstimateVisible ? mMaxEstimate : mMax;
308b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        if (maxVisible <= 0 && mStats != null) {
309b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            // haven't generated path yet; fall back to raw data
310b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            final NetworkStatsHistory.Entry entry = mStats.getValues(mStart, mEnd, null);
311b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            return entry.rxBytes + entry.txBytes;
312b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        } else {
313b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            return maxVisible;
314b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        }
315ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
316ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
317ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
318ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    protected void onDraw(Canvas canvas) {
319ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        int save;
320ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
321b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        if (!mPathValid) {
322b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            generatePath();
323b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        }
324b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
325f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft);
326f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight);
327f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey
328e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        if (mEstimateVisible) {
329e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            save = canvas.save();
330e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.clipRect(0, 0, getWidth(), getHeight());
331e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.drawPath(mPathEstimate, mPaintEstimate);
332e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.restoreToCount(save);
333e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        }
3342412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
3352412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        save = canvas.save();
336f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        canvas.clipRect(0, 0, primaryLeftPoint, getHeight());
33752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        canvas.drawPath(mPathFill, mPaintFillSecondary);
338ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.restoreToCount(save);
339ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
340ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        save = canvas.save();
341f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        canvas.clipRect(primaryRightPoint, 0, getWidth(), getHeight());
34252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        canvas.drawPath(mPathFill, mPaintFillSecondary);
343ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.restoreToCount(save);
344ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
345ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        save = canvas.save();
346f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        canvas.clipRect(primaryLeftPoint, 0, primaryRightPoint, getHeight());
347ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.drawPath(mPathFill, mPaintFill);
348ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.drawPath(mPathStroke, mPaintStroke);
349ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.restoreToCount(save);
350ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
351ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
352ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey}
353