Move UsageGraph from SettingsLib to Settings.
In preparation for modifying the graph code to show detailed projections (see bug), this change moves it into Settings along with related tests and resources. Bug: 38400320 Test: manual, make SettingsUnitTests, make SettingsGoogleUnitTests Change-Id: I88e5336c15827727b3427e29b10954bba9cfba7d
This commit is contained in:
277
src/com/android/settings/graph/UsageGraph.java
Normal file
277
src/com/android/settings/graph/UsageGraph.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.graph;
|
||||
|
||||
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 android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import com.android.settingslib.R;
|
||||
|
||||
public class UsageGraph extends View {
|
||||
|
||||
private static final int PATH_DELIM = -1;
|
||||
|
||||
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();
|
||||
private final int mCornerRadius;
|
||||
|
||||
private int mAccentColor;
|
||||
private boolean mShowProjection;
|
||||
private boolean mProjectUp;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void setMax(int maxX, int maxY) {
|
||||
mMaxX = maxX;
|
||||
mMaxY = maxY;
|
||||
}
|
||||
|
||||
void setDividerLoc(int height) {
|
||||
mMiddleDividerLoc = 1 - height / mMaxY;
|
||||
}
|
||||
|
||||
void setDividerColors(int middleColor, int topColor) {
|
||||
mMiddleDividerTint = middleColor;
|
||||
mTopDividerTint = topColor;
|
||||
}
|
||||
|
||||
public void addPath(SparseIntArray points) {
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
mPaths.put(points.keyAt(i), points.valueAt(i));
|
||||
}
|
||||
mPaths.put(points.keyAt(points.size() - 1) + 1, PATH_DELIM);
|
||||
calculateLocalPaths();
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
void setAccentColor(int color) {
|
||||
mAccentColor = color;
|
||||
mLinePaint.setColor(mAccentColor);
|
||||
updateGradient();
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
void setShowProjection(boolean showProjection, boolean projectUp) {
|
||||
mShowProjection = showProjection;
|
||||
mProjectUp = projectUp;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateGradient();
|
||||
calculateLocalPaths();
|
||||
}
|
||||
|
||||
private void calculateLocalPaths() {
|
||||
if (getWidth() == 0) return;
|
||||
mLocalPaths.clear();
|
||||
int pendingXLoc = 0;
|
||||
int pendingYLoc = PATH_DELIM;
|
||||
for (int i = 0; i < mPaths.size(); i++) {
|
||||
int x = mPaths.keyAt(i);
|
||||
int y = mPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (i == mPaths.size() - 1 && pendingYLoc != PATH_DELIM) {
|
||||
// Connect to the end of the graph.
|
||||
mLocalPaths.put(pendingXLoc, pendingYLoc);
|
||||
}
|
||||
// Clear out any pending points.
|
||||
pendingYLoc = PATH_DELIM;
|
||||
mLocalPaths.put(pendingXLoc + 1, PATH_DELIM);
|
||||
} else {
|
||||
final int lx = getX(x);
|
||||
final int ly = getY(y);
|
||||
pendingXLoc = lx;
|
||||
if (mLocalPaths.size() > 0) {
|
||||
int lastX = mLocalPaths.keyAt(mLocalPaths.size() - 1);
|
||||
int lastY = mLocalPaths.valueAt(mLocalPaths.size() - 1);
|
||||
if (lastY != PATH_DELIM && !hasDiff(lastX, lx) && !hasDiff(lastY, ly)) {
|
||||
pendingYLoc = ly;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mLocalPaths.put(lx, ly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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) {
|
||||
return;
|
||||
}
|
||||
if (mShowProjection) {
|
||||
drawProjection(canvas);
|
||||
}
|
||||
drawFilledPath(canvas);
|
||||
drawLinePath(canvas);
|
||||
}
|
||||
|
||||
private void drawProjection(Canvas canvas) {
|
||||
mPath.reset();
|
||||
int x = mLocalPaths.keyAt(mLocalPaths.size() - 2);
|
||||
int y = mLocalPaths.valueAt(mLocalPaths.size() - 2);
|
||||
mPath.moveTo(x, y);
|
||||
mPath.lineTo(canvas.getWidth(), mProjectUp ? 0 : canvas.getHeight());
|
||||
canvas.drawPath(mPath, mDottedPaint);
|
||||
}
|
||||
|
||||
private void drawLinePath(Canvas canvas) {
|
||||
mPath.reset();
|
||||
mPath.moveTo(mLocalPaths.keyAt(0), mLocalPaths.valueAt(0));
|
||||
for (int i = 1; i < mLocalPaths.size(); i++) {
|
||||
int x = mLocalPaths.keyAt(i);
|
||||
int y = mLocalPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
if (++i < mLocalPaths.size()) {
|
||||
mPath.moveTo(mLocalPaths.keyAt(i), mLocalPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, mLinePaint);
|
||||
}
|
||||
|
||||
private void drawFilledPath(Canvas canvas) {
|
||||
mPath.reset();
|
||||
float lastStartX = mLocalPaths.keyAt(0);
|
||||
mPath.moveTo(mLocalPaths.keyAt(0), mLocalPaths.valueAt(0));
|
||||
for (int i = 1; i < mLocalPaths.size(); i++) {
|
||||
int x = mLocalPaths.keyAt(i);
|
||||
int y = mLocalPaths.valueAt(i);
|
||||
if (y == PATH_DELIM) {
|
||||
mPath.lineTo(mLocalPaths.keyAt(i - 1), getHeight());
|
||||
mPath.lineTo(lastStartX, getHeight());
|
||||
mPath.close();
|
||||
if (++i < mLocalPaths.size()) {
|
||||
lastStartX = mLocalPaths.keyAt(i);
|
||||
mPath.moveTo(mLocalPaths.keyAt(i), mLocalPaths.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
mPath.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(mPath, mFillPaint);
|
||||
}
|
||||
|
||||
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