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