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