Misc clean up. move widgets from graph to widget package.
Bug: n/a Test: robotests, rebuild Change-Id: I910f355312d52e81a0bf57b46a3f267e1eb9882a
This commit is contained in:
315
src/com/android/settings/widget/UsageGraph.java
Normal file
315
src/com/android/settings/widget/UsageGraph.java
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.LinearGradient;
|
||||
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.Shader.TileMode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settingslib.R;
|
||||
|
||||
public class UsageGraph extends View {
|
||||
|
||||
private static final int PATH_DELIM = -1;
|
||||
public static final String LOG_TAG = "UsageGraph";
|
||||
|
||||
private final Paint mLinePaint;
|
||||
private final Paint mFillPaint;
|
||||
private final Paint mDottedPaint;
|
||||
|
||||
private final Drawable mDivider;
|
||||
private final Drawable mTintedDivider;
|
||||
private final int mDividerSize;
|
||||
|
||||
private final Path mPath = new Path();
|
||||
|
||||
// Paths in coordinates they are passed in.
|
||||
private final SparseIntArray mPaths = new SparseIntArray();
|
||||
// Paths in local coordinates for drawing.
|
||||
private final SparseIntArray mLocalPaths = new SparseIntArray();
|
||||
|
||||
// Paths for projection in coordinates they are passed in.
|
||||
private final SparseIntArray mProjectedPaths = new SparseIntArray();
|
||||
// Paths for projection in local coordinates for drawing.
|
||||
private final SparseIntArray mLocalProjectedPaths = new SparseIntArray();
|
||||
|
||||
private final int mCornerRadius;
|
||||
private int mAccentColor;
|
||||
|
||||
private float mMaxX = 100;
|
||||
private float mMaxY = 100;
|
||||
|
||||
private float mMiddleDividerLoc = .5f;
|
||||
private int mMiddleDividerTint = -1;
|
||||
private int mTopDividerTint = -1;
|
||||
|
||||
public UsageGraph(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
mLinePaint = new Paint();
|
||||
mLinePaint.setStyle(Style.STROKE);
|
||||
mLinePaint.setStrokeCap(Cap.ROUND);
|
||||
mLinePaint.setStrokeJoin(Join.ROUND);
|
||||
mLinePaint.setAntiAlias(true);
|
||||
mCornerRadius = resources.getDimensionPixelSize(R.dimen.usage_graph_line_corner_radius);
|
||||
mLinePaint.setPathEffect(new CornerPathEffect(mCornerRadius));
|
||||
mLinePaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.usage_graph_line_width));
|
||||
|
||||
mFillPaint = new Paint(mLinePaint);
|
||||
mFillPaint.setStyle(Style.FILL);
|
||||
|
||||
mDottedPaint = new Paint(mLinePaint);
|
||||
mDottedPaint.setStyle(Style.STROKE);
|
||||
float dots = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_size);
|
||||
float interval = resources.getDimensionPixelSize(R.dimen.usage_graph_dot_interval);
|
||||
mDottedPaint.setStrokeWidth(dots * 3);
|
||||
mDottedPaint.setPathEffect(new DashPathEffect(new float[] {dots, interval}, 0));
|
||||
mDottedPaint.setColor(context.getColor(R.color.usage_graph_dots));
|
||||
|
||||
TypedValue v = new TypedValue();
|
||||
context.getTheme().resolveAttribute(com.android.internal.R.attr.listDivider, v, true);
|
||||
mDivider = context.getDrawable(v.resourceId);
|
||||
mTintedDivider = context.getDrawable(v.resourceId);
|
||||
mDividerSize = resources.getDimensionPixelSize(R.dimen.usage_graph_divider_size);
|
||||
}
|
||||
|
||||
void clearPaths() {
|
||||
mPaths.clear();
|
||||
mLocalPaths.clear();
|
||||
mProjectedPaths.clear();
|
||||
mLocalProjectedPaths.clear();
|
||||
}
|
||||
|
||||
void setMax(int maxX, int maxY) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
mMaxX = maxX;
|
||||
mMaxY = maxY;
|
||||
calculateLocalPaths();
|
||||
postInvalidate();
|
||||
BatteryUtils.logRuntime(LOG_TAG, "setMax", startTime);
|
||||
}
|
||||
|
||||
void setDividerLoc(int height) {
|
||||
mMiddleDividerLoc = 1 - height / mMaxY;
|
||||
}
|
||||
|
||||
void setDividerColors(int middleColor, int topColor) {
|
||||
mMiddleDividerTint = middleColor;
|
||||
mTopDividerTint = topColor;
|
||||
}
|
||||
|
||||
public void addPath(SparseIntArray points) {
|
||||
addPathAndUpdate(points, mPaths, mLocalPaths);
|
||||
}
|
||||
|
||||
public void addProjectedPath(SparseIntArray points) {
|
||||
addPathAndUpdate(points, mProjectedPaths, mLocalProjectedPaths);
|
||||
}
|
||||
|
||||
private void addPathAndUpdate(
|
||||
SparseIntArray points, SparseIntArray paths, SparseIntArray localPaths) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
for (int i = 0, size = points.size(); i < size; i++) {
|
||||
paths.put(points.keyAt(i), points.valueAt(i));
|
||||
}
|
||||
// Add a delimiting value immediately after the last point.
|
||||
paths.put(points.keyAt(points.size() - 1) + 1, PATH_DELIM);
|
||||
calculateLocalPaths(paths, localPaths);
|
||||
postInvalidate();
|
||||
BatteryUtils.logRuntime(LOG_TAG, "addPathAndUpdate", startTime);
|
||||
}
|
||||
|
||||
void setAccentColor(int color) {
|
||||
mAccentColor = color;
|
||||
mLinePaint.setColor(mAccentColor);
|
||||
updateGradient();
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateGradient();
|
||||
calculateLocalPaths();
|
||||
BatteryUtils.logRuntime(LOG_TAG, "onSizeChanged", startTime);
|
||||
}
|
||||
|
||||
private void calculateLocalPaths() {
|
||||
calculateLocalPaths(mPaths, mLocalPaths);
|
||||
calculateLocalPaths(mProjectedPaths, mLocalProjectedPaths);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void calculateLocalPaths(SparseIntArray paths, SparseIntArray localPaths) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
if (getWidth() == 0) {
|
||||
return;
|
||||
}
|
||||
localPaths.clear();
|
||||
// Store the local coordinates of the most recent point.
|
||||
int lx = 0;
|
||||
int ly = PATH_DELIM;
|
||||
boolean skippedLastPoint = false;
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
int x = paths.keyAt(i);
|
||||
int y = paths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (i == 1) {
|
||||
localPaths.put(getX(x+1) - 1, getY(0));
|
||||
continue;
|
||||
}
|
||||
if (i == paths.size() - 1 && skippedLastPoint) {
|
||||
// Add back skipped point to complete the path.
|
||||
localPaths.put(lx, ly);
|
||||
}
|
||||
skippedLastPoint = false;
|
||||
localPaths.put(lx + 1, PATH_DELIM);
|
||||
} else {
|
||||
lx = getX(x);
|
||||
ly = getY(y);
|
||||
// Skip this point if it is not far enough from the last one added.
|
||||
if (localPaths.size() > 0) {
|
||||
int lastX = localPaths.keyAt(localPaths.size() - 1);
|
||||
int lastY = localPaths.valueAt(localPaths.size() - 1);
|
||||
if (lastY != PATH_DELIM && !hasDiff(lastX, lx) && !hasDiff(lastY, ly)) {
|
||||
skippedLastPoint = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
skippedLastPoint = false;
|
||||
localPaths.put(lx, ly);
|
||||
}
|
||||
}
|
||||
BatteryUtils.logRuntime(LOG_TAG, "calculateLocalPaths", startTime);
|
||||
}
|
||||
|
||||
private boolean hasDiff(int x1, int x2) {
|
||||
return Math.abs(x2 - x1) >= mCornerRadius;
|
||||
}
|
||||
|
||||
private int getX(float x) {
|
||||
return (int) (x / mMaxX * getWidth());
|
||||
}
|
||||
|
||||
private int getY(float y) {
|
||||
return (int) (getHeight() * (1 - (y / mMaxY)));
|
||||
}
|
||||
|
||||
private void updateGradient() {
|
||||
mFillPaint.setShader(
|
||||
new LinearGradient(
|
||||
0, 0, 0, getHeight(), getColor(mAccentColor, .2f), 0, TileMode.CLAMP));
|
||||
}
|
||||
|
||||
private int getColor(int color, float alphaScale) {
|
||||
return (color & (((int) (0xff * alphaScale) << 24) | 0xffffff));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
// Draw lines across the top, middle, and bottom.
|
||||
if (mMiddleDividerLoc != 0) {
|
||||
drawDivider(0, canvas, mTopDividerTint);
|
||||
}
|
||||
drawDivider(
|
||||
(int) ((canvas.getHeight() - mDividerSize) * mMiddleDividerLoc),
|
||||
canvas,
|
||||
mMiddleDividerTint);
|
||||
drawDivider(canvas.getHeight() - mDividerSize, canvas, -1);
|
||||
|
||||
if (mLocalPaths.size() == 0 && mLocalProjectedPaths.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawLinePath(canvas, mLocalProjectedPaths, mDottedPaint);
|
||||
drawFilledPath(canvas, mLocalPaths, mFillPaint);
|
||||
drawLinePath(canvas, mLocalPaths, mLinePaint);
|
||||
BatteryUtils.logRuntime(LOG_TAG, "onDraw", startTime);
|
||||
}
|
||||
|
||||
private void drawLinePath(Canvas canvas, SparseIntArray localPaths, Paint paint) {
|
||||
if (localPaths.size() == 0) {
|
||||
return;
|
||||
}
|
||||
mPath.reset();
|
||||
mPath.moveTo(localPaths.keyAt(0), localPaths.valueAt(0));
|
||||
for (int i = 1; i < localPaths.size(); i++) {
|
||||
int x = localPaths.keyAt(i);
|
||||
int y = localPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (++i < localPaths.size()) {
|
||||
mPath.moveTo(localPaths.keyAt(i), localPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, paint);
|
||||
}
|
||||
|
||||
private void drawFilledPath(Canvas canvas, SparseIntArray localPaths, Paint paint) {
|
||||
mPath.reset();
|
||||
float lastStartX = localPaths.keyAt(0);
|
||||
mPath.moveTo(localPaths.keyAt(0), localPaths.valueAt(0));
|
||||
for (int i = 1; i < localPaths.size(); i++) {
|
||||
int x = localPaths.keyAt(i);
|
||||
int y = localPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
mPath.lineTo(localPaths.keyAt(i - 1), getHeight());
|
||||
mPath.lineTo(lastStartX, getHeight());
|
||||
mPath.close();
|
||||
if (++i < localPaths.size()) {
|
||||
lastStartX = localPaths.keyAt(i);
|
||||
mPath.moveTo(localPaths.keyAt(i), localPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, paint);
|
||||
}
|
||||
|
||||
private void drawDivider(int y, Canvas canvas, int tintColor) {
|
||||
Drawable d = mDivider;
|
||||
if (tintColor != -1) {
|
||||
mTintedDivider.setTint(tintColor);
|
||||
d = mTintedDivider;
|
||||
}
|
||||
d.setBounds(0, y, canvas.getWidth(), y + mDividerSize);
|
||||
d.draw(canvas);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user