Files
app_Settings/src/com/android/settings/widget/ChartNetworkSeriesView.java
Jeff Sharkey ebae659fc7 Migrate to refactored NetworkStats API.
Change-Id: I76452a67b74df873c88cb9092188e5e4ba83b991
2011-07-12 13:53:11 -07:00

223 lines
6.9 KiB
Java

/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import android.net.NetworkStatsHistory;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.android.settings.R;
import com.google.common.base.Preconditions;
/**
* {@link NetworkStatsHistory} series to render inside a {@link ChartView},
* using {@link ChartAxis} to map into screen coordinates.
*/
public class ChartNetworkSeriesView extends View {
private static final String TAG = "ChartNetworkSeriesView";
private static final boolean LOGD = true;
private ChartAxis mHoriz;
private ChartAxis mVert;
private Paint mPaintStroke;
private Paint mPaintFill;
private Paint mPaintFillSecondary;
private NetworkStatsHistory mStats;
private Path mPathStroke;
private Path mPathFill;
private long mPrimaryLeft;
private long mPrimaryRight;
public ChartNetworkSeriesView(Context context) {
this(context, null, 0);
}
public ChartNetworkSeriesView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ChartNetworkSeriesView, defStyle, 0);
final int stroke = a.getColor(R.styleable.ChartNetworkSeriesView_strokeColor, Color.RED);
final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED);
final int fillSecondary = a.getColor(
R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED);
setChartColor(stroke, fill, fillSecondary);
setWillNotDraw(false);
a.recycle();
mPathStroke = new Path();
mPathFill = new Path();
}
void init(ChartAxis horiz, ChartAxis vert) {
mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
mVert = Preconditions.checkNotNull(vert, "missing vert");
}
public void setChartColor(int stroke, int fill, int fillSecondary) {
mPaintStroke = new Paint();
mPaintStroke.setStrokeWidth(6.0f);
mPaintStroke.setColor(stroke);
mPaintStroke.setStyle(Style.STROKE);
mPaintStroke.setAntiAlias(true);
mPaintFill = new Paint();
mPaintFill.setColor(fill);
mPaintFill.setStyle(Style.FILL);
mPaintFill.setAntiAlias(true);
mPaintFillSecondary = new Paint();
mPaintFillSecondary.setColor(fillSecondary);
mPaintFillSecondary.setStyle(Style.FILL);
mPaintFillSecondary.setAntiAlias(true);
}
public void bindNetworkStats(NetworkStatsHistory stats) {
mStats = stats;
mPathStroke.reset();
mPathFill.reset();
invalidate();
}
/**
* Set the range to paint with {@link #mPaintFill}, leaving the remaining
* area to be painted with {@link #mPaintFillSecondary}.
*/
public void setPrimaryRange(long left, long right) {
mPrimaryLeft = left;
mPrimaryRight = right;
invalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
generatePath();
}
/**
* Erase any existing {@link Path} and generate series outline based on
* currently bound {@link NetworkStatsHistory} data.
*/
public void generatePath() {
if (LOGD) Log.d(TAG, "generatePath()");
mPathStroke.reset();
mPathFill.reset();
// bail when not enough stats to render
if (mStats == null || mStats.size() < 2) return;
final int width = getWidth();
final int height = getHeight();
boolean started = false;
float firstX = 0;
float lastX = 0;
float lastY = 0;
// TODO: count fractional data from first bucket crossing start;
// currently it only accepts first full bucket.
long totalData = 0;
NetworkStatsHistory.Entry entry = null;
for (int i = 0; i < mStats.size(); i++) {
entry = mStats.getValues(i, entry);
final float x = mHoriz.convertToPoint(entry.bucketStart);
final float y = mVert.convertToPoint(totalData);
// skip until we find first stats on screen
if (i > 0 && !started && x > 0) {
mPathStroke.moveTo(lastX, lastY);
mPathFill.moveTo(lastX, lastY);
started = true;
firstX = x;
}
if (started) {
mPathStroke.lineTo(x, y);
mPathFill.lineTo(x, y);
totalData += entry.rxBytes + entry.txBytes;
}
// skip if beyond view
if (x > width) break;
lastX = x;
lastY = y;
}
if (LOGD) {
final RectF bounds = new RectF();
mPathFill.computeBounds(bounds, true);
Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData="
+ totalData);
}
// drop to bottom of graph from current location
mPathFill.lineTo(lastX, height);
mPathFill.lineTo(firstX, height);
}
@Override
protected void onDraw(Canvas canvas) {
int save;
final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft);
final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight);
save = canvas.save();
canvas.clipRect(0, 0, primaryLeftPoint, getHeight());
canvas.drawPath(mPathFill, mPaintFillSecondary);
canvas.restoreToCount(save);
save = canvas.save();
canvas.clipRect(primaryRightPoint, 0, getWidth(), getHeight());
canvas.drawPath(mPathFill, mPaintFillSecondary);
canvas.restoreToCount(save);
save = canvas.save();
canvas.clipRect(primaryLeftPoint, 0, primaryRightPoint, getHeight());
canvas.drawPath(mPathFill, mPaintFill);
canvas.drawPath(mPathStroke, mPaintStroke);
canvas.restoreToCount(save);
}
}