ChartNetworkSeriesView.java revision 8a50364a71e7c261b54840210f8bacff5abecb34
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.widget; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Color; 22import android.graphics.Paint; 23import android.graphics.Paint.Style; 24import android.graphics.Path; 25import android.graphics.RectF; 26import android.net.NetworkStatsHistory; 27import android.util.Log; 28import android.view.View; 29 30import com.google.common.base.Preconditions; 31 32/** 33 * {@link NetworkStatsHistory} series to render inside a {@link ChartView}, 34 * using {@link ChartAxis} to map into screen coordinates. 35 */ 36public class ChartNetworkSeriesView extends View { 37 private static final String TAG = "ChartNetworkSeriesView"; 38 private static final boolean LOGD = true; 39 40 private final ChartAxis mHoriz; 41 private final ChartAxis mVert; 42 43 private final Paint mPaintStroke; 44 private final Paint mPaintFill; 45 private final Paint mPaintFillDisabled; 46 47 private NetworkStatsHistory mStats; 48 49 private Path mPathStroke; 50 private Path mPathFill; 51 52 private ChartSweepView mSweep1; 53 private ChartSweepView mSweep2; 54 55 public ChartNetworkSeriesView(Context context, ChartAxis horiz, ChartAxis vert) { 56 super(context); 57 58 mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); 59 mVert = Preconditions.checkNotNull(vert, "missing vert"); 60 61 mPaintStroke = new Paint(); 62 mPaintStroke.setStrokeWidth(6.0f); 63 mPaintStroke.setColor(Color.parseColor("#24aae1")); 64 mPaintStroke.setStyle(Style.STROKE); 65 mPaintStroke.setAntiAlias(true); 66 67 mPaintFill = new Paint(); 68 mPaintFill.setColor(Color.parseColor("#c050ade5")); 69 mPaintFill.setStyle(Style.FILL); 70 mPaintFill.setAntiAlias(true); 71 72 mPaintFillDisabled = new Paint(); 73 mPaintFillDisabled.setColor(Color.parseColor("#88566abc")); 74 mPaintFillDisabled.setStyle(Style.FILL); 75 mPaintFillDisabled.setAntiAlias(true); 76 77 mPathStroke = new Path(); 78 mPathFill = new Path(); 79 } 80 81 public void bindNetworkStats(NetworkStatsHistory stats) { 82 mStats = stats; 83 84 mPathStroke.reset(); 85 mPathFill.reset(); 86 } 87 88 public void bindSweepRange(ChartSweepView sweep1, ChartSweepView sweep2) { 89 // TODO: generalize to support vertical sweeps 90 // TODO: enforce that both sweeps are along same dimension 91 92 mSweep1 = Preconditions.checkNotNull(sweep1, "missing sweep1"); 93 mSweep2 = Preconditions.checkNotNull(sweep2, "missing sweep2"); 94 } 95 96 @Override 97 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 98 generatePath(); 99 } 100 101 /** 102 * Erase any existing {@link Path} and generate series outline based on 103 * currently bound {@link NetworkStatsHistory} data. 104 */ 105 public void generatePath() { 106 if (LOGD) Log.d(TAG, "generatePath()"); 107 108 mPathStroke.reset(); 109 mPathFill.reset(); 110 111 // bail when not enough stats to render 112 if (mStats == null || mStats.bucketCount < 2) return; 113 114 final int width = getWidth(); 115 final int height = getHeight(); 116 117 boolean started = false; 118 float firstX = 0; 119 float lastX = 0; 120 float lastY = 0; 121 122 // TODO: count fractional data from first bucket crossing start; 123 // currently it only accepts first full bucket. 124 125 long totalData = 0; 126 127 for (int i = 0; i < mStats.bucketCount; i++) { 128 final float x = mHoriz.convertToPoint(mStats.bucketStart[i]); 129 final float y = mVert.convertToPoint(totalData); 130 131 // skip until we find first stats on screen 132 if (i > 0 && !started && x > 0) { 133 mPathStroke.moveTo(lastX, lastY); 134 mPathFill.moveTo(lastX, lastY); 135 started = true; 136 firstX = x; 137 } 138 139 if (started) { 140 mPathStroke.lineTo(x, y); 141 mPathFill.lineTo(x, y); 142 totalData += mStats.rx[i] + mStats.tx[i]; 143 } 144 145 // skip if beyond view 146 if (x > width) break; 147 148 lastX = x; 149 lastY = y; 150 } 151 152 if (LOGD) { 153 final RectF bounds = new RectF(); 154 mPathFill.computeBounds(bounds, true); 155 Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString()); 156 } 157 158 // drop to bottom of graph from current location 159 mPathFill.lineTo(lastX, height); 160 mPathFill.lineTo(firstX, height); 161 } 162 163 @Override 164 protected void onDraw(Canvas canvas) { 165 166 // clip to sweep area 167 final float sweep1 = mSweep1.getPoint(); 168 final float sweep2 = mSweep2.getPoint(); 169 final float sweepLeft = Math.min(sweep1, sweep2); 170 final float sweepRight = Math.max(sweep1, sweep2); 171 172 int save; 173 174 save = canvas.save(); 175 canvas.clipRect(0, 0, sweepLeft, getHeight()); 176 canvas.drawPath(mPathFill, mPaintFillDisabled); 177 canvas.restoreToCount(save); 178 179 save = canvas.save(); 180 canvas.clipRect(sweepRight, 0, getWidth(), getHeight()); 181 canvas.drawPath(mPathFill, mPaintFillDisabled); 182 canvas.restoreToCount(save); 183 184 save = canvas.save(); 185 canvas.clipRect(sweepLeft, 0, sweepRight, getHeight()); 186 canvas.drawPath(mPathFill, mPaintFill); 187 canvas.drawPath(mPathStroke, mPaintStroke); 188 canvas.restoreToCount(save); 189 190 } 191} 192