package com.kedzie.vbox.metrics; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Join; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.Rect; import android.util.Log; import android.view.View; import com.kedzie.vbox.VBoxApplication; public class MetricRenderer extends View { private static String TAG = "MetricRenderer"; /** # of vertical grid lines */ private static final int GRID_LINES_VERT = 10; /** # of horizontal grid lines */ private static final int GRID_LINES_HORIZ = 10; /** Maximum Y Value */ protected int _max; /** # of data points */ protected int _count; /** Time interval between datapoints */ protected int _period; /** Metric names to render */ protected String[] _metrics; /** pixels/period */ protected int hStep; /** pixels/unit */ protected double vStep; /** Metric data */ protected Map<String, MetricQuery> _data = new HashMap<String, MetricQuery>(); protected String _unit; private Rect bounds = new Rect(); private Paint textPaint = new Paint(), bgPaint = new Paint(), borderPaint = new Paint(), metricPaint = new Paint(), gridPaint = new Paint(), metricFill=new Paint(), _editTextPaint = new Paint(); private Path path = new Path(); private Path hGridPath = new Path(); private Path vGridPath = new Path(); private Bitmap _gridBitmap; public MetricRenderer(Context context, int bgColor, int gridColor, int textColor, int borderColor) { super(context); bgPaint.setStyle(Style.FILL); bgPaint.setColor(bgColor); borderPaint.setStyle(Style.STROKE); borderPaint.setColor(borderColor); borderPaint.setAntiAlias(true); borderPaint.setStrokeWidth(4.0f); textPaint.setColor(textColor); textPaint.setAntiAlias(true); textPaint.setTextSize(18.0f); _editTextPaint.setColor(0xFF000000); _editTextPaint.setAntiAlias(true); _editTextPaint.setTextSize(24.0f); gridPaint.setColor(gridColor); gridPaint.setAntiAlias(true); gridPaint.setStrokeWidth(1.5f); gridPaint.setStyle(Style.STROKE); gridPaint.setPathEffect(new DashPathEffect(new float[] { 5, 15, 4, 8 }, 0)); metricPaint.setStrokeWidth(4.0f); metricPaint.setStrokeJoin(Join.MITER); metricPaint.setStrokeCap(Cap.ROUND); metricPaint.setAntiAlias(true); metricPaint.setStyle(Style.STROKE); metricFill.setStyle(Style.FILL); } public void init( int max, String []metrics) { Log.i(TAG, String.format("Metrics initialized: Max=%1$d Metrics=%2$s", max, Arrays.asList(metrics).toString() )); _max=max; _metrics=metrics; } public void setMetricPrefs(int count, int period) { _count=count; _period=period; vStep = (float)getHeight()/(float)_max; hStep = getWidth()/_count; Log.i(TAG, String.format("Set Metric Preferences period/count: %1$d/%2$d\thStep/vStep: %3$d,%4$.2f",period, count, hStep, vStep )); if(getWidth()>0 && getHeight()>0) { if(_gridBitmap!=null) { _gridBitmap.recycle(); _gridBitmap=null; } _gridBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas gridCanvas = new Canvas(_gridBitmap); drawGrid(gridCanvas); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if(!isInEditMode() && _count>0 && _period>0) setMetricPrefs(_count, _period); bounds = new Rect(); } public synchronized void setQuery(Map<String, MetricQuery> q) { _unit=q.get(_metrics[0]).unit; _data=q; postInvalidate(); } private void drawGrid(Canvas canvas) { canvas.drawRect(bounds, bgPaint); canvas.drawRect(bounds, borderPaint); int hGridStep = _count/GRID_LINES_HORIZ*_period; int hPixelStep = hGridStep*hStep; int horiz = bounds.right; int seconds = 0; hGridPath.reset(); for(int i=1; i<=GRID_LINES_HORIZ; i++) { horiz -= hPixelStep; seconds += hGridStep; hGridPath.moveTo(horiz, bounds.bottom); hGridPath.lineTo(horiz, bounds.top); canvas.drawText(seconds+" sec", horiz, bounds.bottom-20, textPaint); } canvas.drawPath(hGridPath, gridPaint); int yVal = 0; int vert = bounds.bottom; int vValStep = _max/GRID_LINES_VERT; int vPixelStep = (int)(vValStep*vStep); vGridPath.reset(); for( int i=1; i<=GRID_LINES_VERT; i++) { yVal += vValStep; vert -= vPixelStep; vGridPath.moveTo(bounds.left, vert); vGridPath.lineTo(bounds.right, vert); canvas.drawText(yVal+_unit, bounds.left+10, vert+4, textPaint); } canvas.drawPath(vGridPath, gridPaint); } @Override protected synchronized void onDraw(Canvas canvas) { if(bounds.width()==0) { canvas.getClipBounds(bounds); bounds.left+=getPaddingLeft(); bounds.right-=getPaddingRight(); bounds.top+=getPaddingTop(); bounds.bottom-=getPaddingBottom(); Log.v(TAG, String.format("Bounds %1$d,%2$d --> %3$d,%4$d", bounds.left, bounds.top, bounds.right, bounds.bottom)); } if(isInEditMode()) { _editTextPaint.setTextSize(20f); canvas.drawText("Edit Mode", 100, 100, _editTextPaint); return; } canvas.drawBitmap(_gridBitmap, 0, 0, null); for(String metric : _metrics) { if(!_data.containsKey(metric)) continue; String colorName = metric.replace('/', '_').replace("Guest_",""); metricPaint.setColor(VBoxApplication.getInstance().getColor(getContext(), colorName)); metricFill.setColor(VBoxApplication.getInstance().getColor(getContext(), colorName+"_Fill")); int[] data = _data.get(metric).values; int x=bounds.right; path.reset(); path.moveTo(x, bounds.bottom-(int)(data[data.length-1]*vStep)); for(int i=data.length-2; i>=0; i--) { x-=hStep; path.lineTo(x, bounds.bottom-(int)(data[i]*vStep)); } canvas.drawPath(path, metricPaint); //close the path for fill path.lineTo(x, bounds.bottom); path.lineTo(bounds.right, bounds.bottom); path.close(); canvas.drawPath(path, metricFill); } } @Override protected void onDetachedFromWindow() { if(_gridBitmap!=null) { _gridBitmap.recycle(); _gridBitmap=null; } super.onDetachedFromWindow(); } }