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
19ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.content.Context;
2052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.content.res.TypedArray;
21ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Canvas;
22ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Color;
23e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkeyimport android.graphics.DashPathEffect;
24ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint;
25ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint.Style;
26ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Path;
27ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.RectF;
28ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.net.NetworkStatsHistory;
2952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.AttributeSet;
30ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.util.Log;
31ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View;
32ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
33e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkeyimport com.android.internal.util.Preconditions;
3452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport com.android.settings.R;
35ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
3639b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.text.format.DateUtils.DAY_IN_MILLIS;
3739b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.text.format.DateUtils.WEEK_IN_MILLIS;
3839b467482d1bf256a111c757e9b7621c6f523271Jason Monk
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
63b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private int mSafeRegion;
64b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
65b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private long mStart;
66b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private long mEnd;
67b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
68e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    /** Series will be extended to reach this end time. */
69e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private long mEndTime = Long.MIN_VALUE;
70e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
71b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private boolean mPathValid = false;
72e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    private boolean mEstimateVisible = false;
73b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private boolean mSecondary = 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);
96b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final int safeRegion = a.getDimensionPixelSize(
97b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                R.styleable.ChartNetworkSeriesView_safeRegion, 0);
9852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
9952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        setChartColor(stroke, fill, fillSecondary);
100b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        setSafeRegion(safeRegion);
101f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        setWillNotDraw(false);
102ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
10352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        a.recycle();
1044dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey
1054dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPathStroke = new Path();
1064dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPathFill = new Path();
1072412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPathEstimate = new Path();
1084dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey    }
1094dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey
11052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    void init(ChartAxis horiz, ChartAxis vert) {
11152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
11252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mVert = Preconditions.checkNotNull(vert, "missing vert");
11352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
11452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
11552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public void setChartColor(int stroke, int fill, int fillSecondary) {
116ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke = new Paint();
11754d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        mPaintStroke.setStrokeWidth(4.0f * getResources().getDisplayMetrics().density);
1184dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPaintStroke.setColor(stroke);
119ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke.setStyle(Style.STROKE);
120ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintStroke.setAntiAlias(true);
121ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
122ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill = new Paint();
1234dfa66001d20d1b461ef15f94887aa050d9295cfJeff Sharkey        mPaintFill.setColor(fill);
124ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill.setStyle(Style.FILL);
125ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintFill.setAntiAlias(true);
126ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
12752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary = new Paint();
12852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setColor(fillSecondary);
12952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setStyle(Style.FILL);
13052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPaintFillSecondary.setAntiAlias(true);
1312412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
1322412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate = new Paint();
1332412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setStrokeWidth(3.0f);
1342412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setColor(fillSecondary);
1352412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setStyle(Style.STROKE);
1362412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPaintEstimate.setAntiAlias(true);
137e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1));
138ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
139ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
140b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    public void setSafeRegion(int safeRegion) {
141b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mSafeRegion = safeRegion;
142b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    }
143b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
144ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public void bindNetworkStats(NetworkStatsHistory stats) {
145ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mStats = stats;
146b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        invalidatePath();
147f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey        invalidate();
148ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
149ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
150b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    public void setBounds(long start, long end) {
151b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mStart = start;
152b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mEnd = end;
153b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    }
154b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
155b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    public void setSecondary(boolean secondary) {
156b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mSecondary = secondary;
157ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
158ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
159b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    public void invalidatePath() {
160b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mPathValid = false;
161b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mMax = 0;
162b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        invalidate();
163ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
164ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
165ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    /**
166ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     * Erase any existing {@link Path} and generate series outline based on
167ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     * currently bound {@link NetworkStatsHistory} data.
168ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey     */
169b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey    private void generatePath() {
1708a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        if (LOGD) Log.d(TAG, "generatePath()");
1718a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey
172e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mMax = 0;
173ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathStroke.reset();
174ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathFill.reset();
1752412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey        mPathEstimate.reset();
176b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        mPathValid = true;
177ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
178ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // bail when not enough stats to render
17954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        if (mStats == null || mStats.size() < 2) {
18054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey            return;
18154d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        }
182ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
183ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int width = getWidth();
184ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int height = getHeight();
185ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
186ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        boolean started = false;
187ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        float lastX = 0;
188f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        float lastY = height;
189f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        long lastTime = mHoriz.convertToValue(lastX);
190f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
191f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        // move into starting position
192f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathStroke.moveTo(lastX, lastY);
193f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathFill.moveTo(lastX, lastY);
194ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
1958a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        // TODO: count fractional data from first bucket crossing start;
1968a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey        // currently it only accepts first full bucket.
1978a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey
198ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        long totalData = 0;
199ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
200ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey        NetworkStatsHistory.Entry entry = null;
201b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
202b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final int start = mStats.getIndexBefore(mStart);
203b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final int end = mStats.getIndexAfter(mEnd);
204b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        for (int i = start; i <= end; i++) {
205ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey            entry = mStats.getValues(i, entry);
206ebae659fc786a14a0dc6ceda2af80fc48e46e123Jeff Sharkey
207f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final long startTime = entry.bucketStart;
208f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final long endTime = startTime + entry.bucketDuration;
209f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
210f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float startX = mHoriz.convertToPoint(startTime);
211f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float endX = mHoriz.convertToPoint(endTime);
212ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
213ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            // skip until we find first stats on screen
214f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            if (endX < 0) continue;
215f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
216f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            // increment by current bucket total
217f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            totalData += entry.rxBytes + entry.txBytes;
218ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
219f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float startY = lastY;
220f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            final float endY = mVert.convertToPoint(totalData);
221f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
222f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            if (lastTime != startTime) {
223f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                // gap in buckets; line to start of current bucket
224f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                mPathStroke.lineTo(startX, startY);
225f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey                mPathFill.lineTo(startX, startY);
226ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
227ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
228f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            // always draw to end of current bucket
229f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathStroke.lineTo(endX, endY);
230f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathFill.lineTo(endX, endY);
231f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey
232f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastX = endX;
233f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastY = endY;
234f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            lastTime = endTime;
235ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
236ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
237e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        // when data falls short, extend to requested end time
238e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        if (lastTime < mEndTime) {
239e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            lastX = mHoriz.convertToPoint(mEndTime);
240e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
241f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathStroke.lineTo(lastX, lastY);
242f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey            mPathFill.lineTo(lastX, lastY);
243e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        }
244e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
245ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        if (LOGD) {
246ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final RectF bounds = new RectF();
247ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            mPathFill.computeBounds(bounds, true);
2488e911d7b1a6817f67480b7677f8d36ab3bfb00f2Jeff Sharkey            Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData="
2498e911d7b1a6817f67480b7677f8d36ab3bfb00f2Jeff Sharkey                    + totalData);
250ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
251ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
252ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // drop to bottom of graph from current location
253ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPathFill.lineTo(lastX, height);
254f9eca2e0e0dc31fde40cc731c50c3f522f578c69Jeff Sharkey        mPathFill.lineTo(0, height);
2552412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
256e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mMax = totalData;
257e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
258a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        if (ESTIMATE_ENABLED) {
259a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            // build estimated data
260a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            mPathEstimate.moveTo(lastX, lastY);
2612412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
262a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long now = System.currentTimeMillis();
263a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long bucketDuration = mStats.getBucketDuration();
2642412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
265a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            // long window is average over two weeks
266a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            entry = mStats.getValues(lastTime - WEEK_IN_MILLIS * 2, lastTime, now, entry);
267a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            final long longWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
268a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                    / entry.bucketDuration;
2692412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
270a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            long futureTime = 0;
271a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            while (lastX < width) {
272a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                futureTime += bucketDuration;
2732412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
274a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                // short window is day average last week
275a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                final long lastWeekTime = lastTime - WEEK_IN_MILLIS + (futureTime % WEEK_IN_MILLIS);
276a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                entry = mStats.getValues(lastWeekTime - DAY_IN_MILLIS, lastWeekTime, now, entry);
277a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                final long shortWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
278a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                        / entry.bucketDuration;
2792412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
280a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                totalData += (longWindow * 7 + shortWindow * 3) / 10;
2812412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
282a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                lastX = mHoriz.convertToPoint(lastTime + futureTime);
283a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                lastY = mVert.convertToPoint(totalData);
2842412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
285a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey                mPathEstimate.lineTo(lastX, lastY);
286a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            }
287e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
288a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey            mMaxEstimate = totalData;
289a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        }
29054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey
29154d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey        invalidate();
292e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
293e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
294e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public void setEndTime(long endTime) {
295e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        mEndTime = endTime;
296e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
297e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
298e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public void setEstimateVisible(boolean estimateVisible) {
299a8106f248c36dd7800927489d4b43e8df30ed808Jeff Sharkey        mEstimateVisible = ESTIMATE_ENABLED ? estimateVisible : false;
300e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        invalidate();
301e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
302e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
303e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public long getMaxEstimate() {
304e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        return mMaxEstimate;
305e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    }
306e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey
307e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey    public long getMaxVisible() {
308b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        final long maxVisible = mEstimateVisible ? mMaxEstimate : mMax;
309b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        if (maxVisible <= 0 && mStats != null) {
310b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            // haven't generated path yet; fall back to raw data
311b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            final NetworkStatsHistory.Entry entry = mStats.getValues(mStart, mEnd, null);
312b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            return entry.rxBytes + entry.txBytes;
313b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        } else {
314b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            return maxVisible;
315b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        }
316ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
317ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
318ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
319ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    protected void onDraw(Canvas canvas) {
320ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        int save;
321ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
322b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        if (!mPathValid) {
323b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey            generatePath();
324b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey        }
325b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey
326e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        if (mEstimateVisible) {
327e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            save = canvas.save();
328e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.clipRect(0, 0, getWidth(), getHeight());
329e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.drawPath(mPathEstimate, mPaintEstimate);
330e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey            canvas.restoreToCount(save);
331e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey        }
3322412b0f2489d5fde811d802b39a0a00d861b5ddfJeff Sharkey
333b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final Paint paintFill = mSecondary ? mPaintFillSecondary : mPaintFill;
334ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
335ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        save = canvas.save();
336b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        canvas.clipRect(mSafeRegion, 0, getWidth(), getHeight() - mSafeRegion);
337b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        canvas.drawPath(mPathFill, paintFill);
338ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        canvas.restoreToCount(save);
339ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
340ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey}
341