1c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza/* 2c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * Copyright (C) 2016 The Android Open Source Project 3c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * 4c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * except in compliance with the License. You may obtain a copy of the License at 6c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * 7c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * http://www.apache.org/licenses/LICENSE-2.0 8c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * 9c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * Unless required by applicable law or agreed to in writing, software distributed under the 10c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * KIND, either express or implied. See the License for the specific language governing 12c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza * permissions and limitations under the License. 13c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza */ 14c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 15c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszapackage com.android.settings.graph; 16c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 17c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.annotation.Nullable; 18c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.content.Context; 19c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.content.res.Resources; 20c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Canvas; 21c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.CornerPathEffect; 22c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.DashPathEffect; 23c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.LinearGradient; 24c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Paint; 25c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Paint.Cap; 26c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Paint.Join; 27c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Paint.Style; 28c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Path; 29c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.Shader.TileMode; 30c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.graphics.drawable.Drawable; 314a121ecfaed6393de254673a2d9d4bd74e475042Alex Kuleszaimport android.support.annotation.VisibleForTesting; 32c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.util.AttributeSet; 33c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.util.SparseIntArray; 34c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.util.TypedValue; 35c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport android.view.View; 36c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza 3779616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinezimport com.android.settings.fuelgauge.BatteryUtils; 38c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszaimport com.android.settingslib.R; 39c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 40c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kuleszapublic class UsageGraph extends View { 41c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 42c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private static final int PATH_DELIM = -1; 4379616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez public static final String LOG_TAG = "UsageGraph"; 44c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 45c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Paint mLinePaint; 46c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Paint mFillPaint; 47c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Paint mDottedPaint; 48c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 49c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Drawable mDivider; 50c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Drawable mTintedDivider; 51c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final int mDividerSize; 52c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 53c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final Path mPath = new Path(); 54c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 55c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza // Paths in coordinates they are passed in. 56c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final SparseIntArray mPaths = new SparseIntArray(); 57c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza // Paths in local coordinates for drawing. 58c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private final SparseIntArray mLocalPaths = new SparseIntArray(); 59c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 60c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza // Paths for projection in coordinates they are passed in. 61c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza private final SparseIntArray mProjectedPaths = new SparseIntArray(); 62c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza // Paths for projection in local coordinates for drawing. 63c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza private final SparseIntArray mLocalProjectedPaths = new SparseIntArray(); 64c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza 65c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza private final int mCornerRadius; 66c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int mAccentColor; 67c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 68c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private float mMaxX = 100; 69c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private float mMaxY = 100; 70c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 71c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private float mMiddleDividerLoc = .5f; 72c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int mMiddleDividerTint = -1; 73c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int mTopDividerTint = -1; 74c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 75c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza public UsageGraph(Context context, @Nullable AttributeSet attrs) { 76c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza super(context, attrs); 77c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza final Resources resources = context.getResources(); 78c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 79c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint = new Paint(); 80c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setStyle(Style.STROKE); 81c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setStrokeCap(Cap.ROUND); 82c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setStrokeJoin(Join.ROUND); 83c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setAntiAlias(true); 84c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mCornerRadius = resources.getDimensionPixelSize(R.dimen.usage_graph_line_corner_radius); 85c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setPathEffect(new CornerPathEffect(mCornerRadius)); 86c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.usage_graph_line_width)); 87c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 88c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mFillPaint = new Paint(mLinePaint); 89c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mFillPaint.setStyle(Style.FILL); 90c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 91c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDottedPaint = new Paint(mLinePaint); 92c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDottedPaint.setStyle(Style.STROKE); 93c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza float dots = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_size); 94c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza float interval = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_interval); 95c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDottedPaint.setStrokeWidth(dots * 3); 964a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza mDottedPaint.setPathEffect(new DashPathEffect(new float[] {dots, interval}, 0)); 97c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDottedPaint.setColor(context.getColor(R.color.usage_graph_dots)); 98c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 99c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza TypedValue v = new TypedValue(); 100c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza context.getTheme().resolveAttribute(com.android.internal.R.attr.listDivider, v, true); 101c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDivider = context.getDrawable(v.resourceId); 102c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mTintedDivider = context.getDrawable(v.resourceId); 103c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mDividerSize = resources.getDimensionPixelSize(R.dimen.usage_graph_divider_size); 104c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 105c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 106c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza void clearPaths() { 107c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPaths.clear(); 108c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mLocalPaths.clear(); 109c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mProjectedPaths.clear(); 110c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mLocalProjectedPaths.clear(); 111c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 112c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 113c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza void setMax(int maxX, int maxY) { 11479616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez final long startTime = System.currentTimeMillis(); 115c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mMaxX = maxX; 116c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mMaxY = maxY; 11782dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza calculateLocalPaths(); 11882dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza postInvalidate(); 11979616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez BatteryUtils.logRuntime(LOG_TAG, "setMax", startTime); 120c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 121c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 122c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza void setDividerLoc(int height) { 123c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mMiddleDividerLoc = 1 - height / mMaxY; 124c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 125c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 126c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza void setDividerColors(int middleColor, int topColor) { 127c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mMiddleDividerTint = middleColor; 128c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mTopDividerTint = topColor; 129c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 130c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 131c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza public void addPath(SparseIntArray points) { 132c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza addPathAndUpdate(points, mPaths, mLocalPaths); 133c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza } 134c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza 135c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza public void addProjectedPath(SparseIntArray points) { 136c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza addPathAndUpdate(points, mProjectedPaths, mLocalProjectedPaths); 137c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza } 138c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza 1394a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza private void addPathAndUpdate( 1404a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza SparseIntArray points, SparseIntArray paths, SparseIntArray localPaths) { 14179616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez final long startTime = System.currentTimeMillis(); 142c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza for (int i = 0, size = points.size(); i < size; i++) { 143c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza paths.put(points.keyAt(i), points.valueAt(i)); 144c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 145c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza // Add a delimiting value immediately after the last point. 146c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza paths.put(points.keyAt(points.size() - 1) + 1, PATH_DELIM); 147c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza calculateLocalPaths(paths, localPaths); 148c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza postInvalidate(); 14979616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez BatteryUtils.logRuntime(LOG_TAG, "addPathAndUpdate", startTime); 150c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 151c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 152c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza void setAccentColor(int color) { 153c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mAccentColor = color; 154c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mLinePaint.setColor(mAccentColor); 155c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza updateGradient(); 156c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza postInvalidate(); 157c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 158c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 159c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza @Override 160c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza protected void onSizeChanged(int w, int h, int oldw, int oldh) { 16179616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez final long startTime = System.currentTimeMillis(); 162c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza super.onSizeChanged(w, h, oldw, oldh); 163c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza updateGradient(); 16482dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza calculateLocalPaths(); 16579616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez BatteryUtils.logRuntime(LOG_TAG, "onSizeChanged", startTime); 16682dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza } 16782dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza 16882dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza private void calculateLocalPaths() { 169c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza calculateLocalPaths(mPaths, mLocalPaths); 170c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza calculateLocalPaths(mProjectedPaths, mLocalProjectedPaths); 171c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 172c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 1734a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza @VisibleForTesting 1744a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza void calculateLocalPaths(SparseIntArray paths, SparseIntArray localPaths) { 17579616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez final long startTime = System.currentTimeMillis(); 176c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza if (getWidth() == 0) { 177c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza return; 178c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza } 179c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza localPaths.clear(); 1804a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza // Store the local coordinates of the most recent point. 1814a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza int lx = 0; 1824a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza int ly = PATH_DELIM; 1834a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza boolean skippedLastPoint = false; 184c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza for (int i = 0; i < paths.size(); i++) { 185c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int x = paths.keyAt(i); 186c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int y = paths.valueAt(i); 187c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (y == PATH_DELIM) { 1884a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza if (i == paths.size() - 1 && skippedLastPoint) { 1894a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza // Add back skipped point to complete the path. 1904a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza localPaths.put(lx, ly); 191c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 1924a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza skippedLastPoint = false; 1934a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza localPaths.put(lx + 1, PATH_DELIM); 194c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } else { 1954a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza lx = getX(x); 1964a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza ly = getY(y); 1974a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza // Skip this point if it is not far enough from the last one added. 198c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza if (localPaths.size() > 0) { 199c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int lastX = localPaths.keyAt(localPaths.size() - 1); 200c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int lastY = localPaths.valueAt(localPaths.size() - 1); 201c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (lastY != PATH_DELIM && !hasDiff(lastX, lx) && !hasDiff(lastY, ly)) { 2024a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza skippedLastPoint = true; 203c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza continue; 204c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 205c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 2064a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza skippedLastPoint = false; 207c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza localPaths.put(lx, ly); 208c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 209c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 2104a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza BatteryUtils.logRuntime(LOG_TAG, "calculateLocalPaths", startTime); 211c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 212c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 213c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private boolean hasDiff(int x1, int x2) { 214c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza return Math.abs(x2 - x1) >= mCornerRadius; 215c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 216c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 217c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int getX(float x) { 218c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza return (int) (x / mMaxX * getWidth()); 219c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 220c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 221c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int getY(float y) { 222c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza return (int) (getHeight() * (1 - (y / mMaxY))); 223c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 224c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 225c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private void updateGradient() { 226c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mFillPaint.setShader( 2274a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza new LinearGradient( 2284a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza 0, 0, 0, getHeight(), getColor(mAccentColor, .2f), 0, TileMode.CLAMP)); 229c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 230c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 231c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private int getColor(int color, float alphaScale) { 232c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza return (color & (((int) (0xff * alphaScale) << 24) | 0xffffff)); 233c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 234c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 235c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza @Override 236c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza protected void onDraw(Canvas canvas) { 23779616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez final long startTime = System.currentTimeMillis(); 238c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza // Draw lines across the top, middle, and bottom. 239c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (mMiddleDividerLoc != 0) { 240c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza drawDivider(0, canvas, mTopDividerTint); 241c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 2424a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza drawDivider( 2434a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza (int) ((canvas.getHeight() - mDividerSize) * mMiddleDividerLoc), 2444a121ecfaed6393de254673a2d9d4bd74e475042Alex Kulesza canvas, 245c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mMiddleDividerTint); 246c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza drawDivider(canvas.getHeight() - mDividerSize, canvas, -1); 247c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 24882dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza if (mLocalPaths.size() == 0 && mLocalProjectedPaths.size() == 0) { 249c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza return; 250c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 25182dbcd973dc8b3aa73c877cc53ef538c62cb8c75Alex Kulesza 252c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza drawLinePath(canvas, mLocalProjectedPaths, mDottedPaint); 253c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza drawFilledPath(canvas, mLocalPaths, mFillPaint); 254c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza drawLinePath(canvas, mLocalPaths, mLinePaint); 25579616276808d1c1d4d32e9cb8d2c87a08b01e8f2Salvador Martinez BatteryUtils.logRuntime(LOG_TAG, "onDraw", startTime); 256c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 257c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 258c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza private void drawLinePath(Canvas canvas, SparseIntArray localPaths, Paint paint) { 259c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza if (localPaths.size() == 0) { 260c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza return; 261c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza } 262c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.reset(); 263c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mPath.moveTo(localPaths.keyAt(0), localPaths.valueAt(0)); 264c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza for (int i = 1; i < localPaths.size(); i++) { 265c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int x = localPaths.keyAt(i); 266c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int y = localPaths.valueAt(i); 267c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (y == PATH_DELIM) { 268c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza if (++i < localPaths.size()) { 269c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mPath.moveTo(localPaths.keyAt(i), localPaths.valueAt(i)); 270c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 271c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } else { 272c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.lineTo(x, y); 273c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 274c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 275c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza canvas.drawPath(mPath, paint); 276c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 277c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 278c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza private void drawFilledPath(Canvas canvas, SparseIntArray localPaths, Paint paint) { 279c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.reset(); 280c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza float lastStartX = localPaths.keyAt(0); 281c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mPath.moveTo(localPaths.keyAt(0), localPaths.valueAt(0)); 282c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza for (int i = 1; i < localPaths.size(); i++) { 283c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int x = localPaths.keyAt(i); 284c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza int y = localPaths.valueAt(i); 285c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (y == PATH_DELIM) { 286c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mPath.lineTo(localPaths.keyAt(i - 1), getHeight()); 287c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.lineTo(lastStartX, getHeight()); 288c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.close(); 289c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza if (++i < localPaths.size()) { 290c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza lastStartX = localPaths.keyAt(i); 291c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza mPath.moveTo(localPaths.keyAt(i), localPaths.valueAt(i)); 292c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 293c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } else { 294c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mPath.lineTo(x, y); 295c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 296c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 297c57ceaaa8cfc033bb397eab7af0b4359befbef52Alex Kulesza canvas.drawPath(mPath, paint); 298c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 299c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza 300c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza private void drawDivider(int y, Canvas canvas, int tintColor) { 301c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza Drawable d = mDivider; 302c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza if (tintColor != -1) { 303c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza mTintedDivider.setTint(tintColor); 304c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza d = mTintedDivider; 305c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 306c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza d.setBounds(0, y, canvas.getWidth(), y + mDividerSize); 307c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza d.draw(canvas); 308c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza } 309c661098ab8e278c57f1ad1a9adb8f1bbd80a81a5Alex Kulesza} 310