summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/widget
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-07-21 15:38:06 -0700
committerJeff Sharkey <jsharkey@android.com>2014-07-22 16:24:37 -0700
commitb654846300f79e9e4c605ce62d09f9a05d232fee (patch)
tree7680adbda0d42bc2ad3ade1a5b03b5e925de344b /src/com/android/settings/widget
parent47d27bd629838ded2d217f238b6e3b21d18485da (diff)
downloadpackages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.zip
packages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.tar.gz
packages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.tar.bz2
Data Usage, materialized!
Asset drop so Data Usage matches new Material spec. Removes time dimension sweeps, so we now summarize the entire visible axis. Fix time axis labels to not draw outside clip bounds. Remove pie chart. No more checkboxes in menus. Telephony items like roaming are moved back to cellular settings. Start wiring up multi-SIM support. Bug: 15760500, 16019700, 16289924, 16303795 Change-Id: Ie8f4821962319bb82ff4bc2f13f1f66ba1bdfe65
Diffstat (limited to 'src/com/android/settings/widget')
-rw-r--r--src/com/android/settings/widget/ChartDataUsageView.java93
-rw-r--r--src/com/android/settings/widget/ChartGridView.java94
-rw-r--r--src/com/android/settings/widget/ChartNetworkSeriesView.java42
-rw-r--r--src/com/android/settings/widget/ChartSweepView.java9
-rw-r--r--src/com/android/settings/widget/ChartView.java9
-rw-r--r--src/com/android/settings/widget/PieChartView.java244
6 files changed, 102 insertions, 389 deletions
diff --git a/src/com/android/settings/widget/ChartDataUsageView.java b/src/com/android/settings/widget/ChartDataUsageView.java
index 4e16bfc..c20a8db 100644
--- a/src/com/android/settings/widget/ChartDataUsageView.java
+++ b/src/com/android/settings/widget/ChartDataUsageView.java
@@ -50,26 +50,24 @@ public class ChartDataUsageView extends ChartView {
private static final int MSG_UPDATE_AXIS = 100;
private static final long DELAY_MILLIS = 250;
- private static final boolean LIMIT_SWEEPS_TO_VALID_DATA = false;
-
private ChartGridView mGrid;
private ChartNetworkSeriesView mSeries;
private ChartNetworkSeriesView mDetailSeries;
private NetworkStatsHistory mHistory;
- private ChartSweepView mSweepLeft;
- private ChartSweepView mSweepRight;
private ChartSweepView mSweepWarning;
private ChartSweepView mSweepLimit;
+ private long mInspectStart;
+ private long mInspectEnd;
+
private Handler mHandler;
/** Current maximum value of {@link #mVert}. */
private long mVertMax;
public interface DataUsageChartListener {
- public void onInspectRangeChanged();
public void onWarningChanged();
public void onLimitChanged();
public void requestWarningEdit();
@@ -112,43 +110,27 @@ public class ChartDataUsageView extends ChartView {
mDetailSeries = (ChartNetworkSeriesView) findViewById(R.id.detail_series);
mDetailSeries.setVisibility(View.GONE);
- mSweepLeft = (ChartSweepView) findViewById(R.id.sweep_left);
- mSweepRight = (ChartSweepView) findViewById(R.id.sweep_right);
mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit);
mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning);
// prevent sweeps from crossing each other
- mSweepLeft.setValidRangeDynamic(null, mSweepRight);
- mSweepRight.setValidRangeDynamic(mSweepLeft, null);
mSweepWarning.setValidRangeDynamic(null, mSweepLimit);
mSweepLimit.setValidRangeDynamic(mSweepWarning, null);
// mark neighbors for checking touch events against
- mSweepLeft.setNeighbors(mSweepRight);
- mSweepRight.setNeighbors(mSweepLeft);
- mSweepLimit.setNeighbors(mSweepWarning, mSweepLeft, mSweepRight);
- mSweepWarning.setNeighbors(mSweepLimit, mSweepLeft, mSweepRight);
+ mSweepLimit.setNeighbors(mSweepWarning);
+ mSweepWarning.setNeighbors(mSweepLimit);
- mSweepLeft.addOnSweepListener(mHorizListener);
- mSweepRight.addOnSweepListener(mHorizListener);
mSweepWarning.addOnSweepListener(mVertListener);
mSweepLimit.addOnSweepListener(mVertListener);
mSweepWarning.setDragInterval(5 * MB_IN_BYTES);
mSweepLimit.setDragInterval(5 * MB_IN_BYTES);
- // TODO: make time sweeps adjustable through dpad
- mSweepLeft.setClickable(false);
- mSweepLeft.setFocusable(false);
- mSweepRight.setClickable(false);
- mSweepRight.setFocusable(false);
-
// tell everyone about our axis
mGrid.init(mHoriz, mVert);
mSeries.init(mHoriz, mVert);
mDetailSeries.init(mHoriz, mVert);
- mSweepLeft.init(mHoriz);
- mSweepRight.init(mHoriz);
mSweepWarning.init(mVert);
mSweepLimit.init(mVert);
@@ -194,7 +176,7 @@ public class ChartDataUsageView extends ChartView {
mSweepLimit.setEnabled(true);
mSweepLimit.setValue(policy.limitBytes);
} else {
- mSweepLimit.setVisibility(View.VISIBLE);
+ mSweepLimit.setVisibility(View.INVISIBLE);
mSweepLimit.setEnabled(false);
mSweepLimit.setValue(-1);
}
@@ -295,23 +277,6 @@ public class ChartDataUsageView extends ChartView {
mSeries.setEstimateVisible(estimateVisible);
}
- private OnSweepListener mHorizListener = new OnSweepListener() {
- @Override
- public void onSweep(ChartSweepView sweep, boolean sweepDone) {
- updatePrimaryRange();
-
- // update detail list only when done sweeping
- if (sweepDone && mListener != null) {
- mListener.onInspectRangeChanged();
- }
- }
-
- @Override
- public void requestEdit(ChartSweepView sweep) {
- // ignored
- }
- };
-
private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) {
if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) {
mHandler.sendMessageDelayed(
@@ -369,11 +334,11 @@ public class ChartDataUsageView extends ChartView {
}
public long getInspectStart() {
- return mSweepLeft.getValue();
+ return mInspectStart;
}
public long getInspectEnd() {
- return mSweepRight.getValue();
+ return mInspectEnd;
}
public long getWarningBytes() {
@@ -384,14 +349,6 @@ public class ChartDataUsageView extends ChartView {
return mSweepLimit.getLabelValue();
}
- private long getHistoryStart() {
- return mHistory != null ? mHistory.getStart() : Long.MAX_VALUE;
- }
-
- private long getHistoryEnd() {
- return mHistory != null ? mHistory.getEnd() : Long.MIN_VALUE;
- }
-
/**
* Set the exact time range that should be displayed, updating how
* {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the
@@ -403,30 +360,8 @@ public class ChartDataUsageView extends ChartView {
mSeries.setBounds(visibleStart, visibleEnd);
mDetailSeries.setBounds(visibleStart, visibleEnd);
- final long historyStart = getHistoryStart();
- final long historyEnd = getHistoryEnd();
-
- final long validStart = historyStart == Long.MAX_VALUE ? visibleStart
- : Math.max(visibleStart, historyStart);
- final long validEnd = historyEnd == Long.MIN_VALUE ? visibleEnd
- : Math.min(visibleEnd, historyEnd);
-
- if (LIMIT_SWEEPS_TO_VALID_DATA) {
- // prevent time sweeps from leaving valid data
- mSweepLeft.setValidRange(validStart, validEnd);
- mSweepRight.setValidRange(validStart, validEnd);
- } else {
- mSweepLeft.setValidRange(visibleStart, visibleEnd);
- mSweepRight.setValidRange(visibleStart, visibleEnd);
- }
-
- // default sweeps to last week of data
- final long halfRange = (visibleEnd + visibleStart) / 2;
- final long sweepMax = validEnd;
- final long sweepMin = Math.max(visibleStart, (sweepMax - DateUtils.WEEK_IN_MILLIS));
-
- mSweepLeft.setValue(sweepMin);
- mSweepRight.setValue(sweepMax);
+ mInspectStart = visibleStart;
+ mInspectEnd = visibleEnd;
requestLayout();
if (changed) {
@@ -440,15 +375,11 @@ public class ChartDataUsageView extends ChartView {
}
private void updatePrimaryRange() {
- final long left = mSweepLeft.getValue();
- final long right = mSweepRight.getValue();
-
// prefer showing primary range on detail series, when available
if (mDetailSeries.getVisibility() == View.VISIBLE) {
- mDetailSeries.setPrimaryRange(left, right);
- mSeries.setPrimaryRange(0, 0);
+ mSeries.setSecondary(true);
} else {
- mSeries.setPrimaryRange(left, right);
+ mSeries.setSecondary(false);
}
}
diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java
index ec5882c..4cd6f5f 100644
--- a/src/com/android/settings/widget/ChartGridView.java
+++ b/src/com/android/settings/widget/ChartGridView.java
@@ -19,22 +19,25 @@ package com.android.settings.widget;
import static com.android.settings.DataUsageSummary.formatDateRange;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
+import java.util.Locale;
+
/**
* Background of {@link ChartView} that renders grid lines as requested by
* {@link ChartAxis#getTickPoints()}.
@@ -47,10 +50,13 @@ public class ChartGridView extends View {
private Drawable mPrimary;
private Drawable mSecondary;
private Drawable mBorder;
+
+ private int mLabelSize;
private int mLabelColor;
- private Layout mLayoutStart;
- private Layout mLayoutEnd;
+ private Layout mLabelStart;
+ private Layout mLabelMid;
+ private Layout mLabelEnd;
public ChartGridView(Context context) {
this(context, null, 0);
@@ -71,7 +77,17 @@ public class ChartGridView extends View {
mPrimary = a.getDrawable(R.styleable.ChartGridView_primaryDrawable);
mSecondary = a.getDrawable(R.styleable.ChartGridView_secondaryDrawable);
mBorder = a.getDrawable(R.styleable.ChartGridView_borderDrawable);
- mLabelColor = a.getColor(R.styleable.ChartGridView_labelColor, Color.RED);
+
+ final int taId = a.getResourceId(R.styleable.ChartGridView_android_textAppearance, -1);
+ final TypedArray ta = context.obtainStyledAttributes(taId,
+ com.android.internal.R.styleable.TextAppearance);
+ mLabelSize = ta.getDimensionPixelSize(
+ com.android.internal.R.styleable.TextAppearance_textSize, 0);
+ ta.recycle();
+
+ final ColorStateList labelColor = a.getColorStateList(
+ R.styleable.ChartGridView_android_textColor);
+ mLabelColor = labelColor.getDefaultColor();
a.recycle();
}
@@ -83,71 +99,83 @@ public class ChartGridView extends View {
void setBounds(long start, long end) {
final Context context = getContext();
- mLayoutStart = makeLayout(formatDateRange(context, start, start));
- mLayoutEnd = makeLayout(formatDateRange(context, end, end));
+ final long mid = (start + end) / 2;
+ mLabelStart = makeLabel(formatDateRange(context, start, start));
+ mLabelMid = makeLabel(formatDateRange(context, mid, mid));
+ mLabelEnd = makeLabel(formatDateRange(context, end, end));
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
final int width = getWidth();
- final int height = getHeight();
+ final int height = getHeight() - getPaddingBottom();
final Drawable secondary = mSecondary;
- final int secondaryHeight = mSecondary.getIntrinsicHeight();
-
- final float[] vertTicks = mVert.getTickPoints();
- for (float y : vertTicks) {
- final int bottom = (int) Math.min(y + secondaryHeight, height);
- secondary.setBounds(0, (int) y, width, bottom);
- secondary.draw(canvas);
+ if (secondary != null) {
+ final int secondaryHeight = secondary.getIntrinsicHeight();
+
+ final float[] vertTicks = mVert.getTickPoints();
+ for (float y : vertTicks) {
+ final int bottom = (int) Math.min(y + secondaryHeight, height);
+ secondary.setBounds(0, (int) y, width, bottom);
+ secondary.draw(canvas);
+ }
}
final Drawable primary = mPrimary;
- final int primaryWidth = mPrimary.getIntrinsicWidth();
- final int primaryHeight = mPrimary.getIntrinsicHeight();
-
- final float[] horizTicks = mHoriz.getTickPoints();
- for (float x : horizTicks) {
- final int right = (int) Math.min(x + primaryWidth, width);
- primary.setBounds((int) x, 0, right, height);
- primary.draw(canvas);
+ if (primary != null) {
+ final int primaryWidth = primary.getIntrinsicWidth();
+ final int primaryHeight = primary.getIntrinsicHeight();
+
+ final float[] horizTicks = mHoriz.getTickPoints();
+ for (float x : horizTicks) {
+ final int right = (int) Math.min(x + primaryWidth, width);
+ primary.setBounds((int) x, 0, right, height);
+ primary.draw(canvas);
+ }
}
mBorder.setBounds(0, 0, width, height);
mBorder.draw(canvas);
- final int padding = mLayoutStart != null ? mLayoutStart.getHeight() / 8 : 0;
+ final int padding = mLabelStart != null ? mLabelStart.getHeight() / 8 : 0;
- final Layout start = mLayoutStart;
+ final Layout start = mLabelStart;
if (start != null) {
- canvas.save();
+ final int saveCount = canvas.save();
canvas.translate(0, height + padding);
start.draw(canvas);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
+ }
+
+ final Layout mid = mLabelMid;
+ if (mid != null) {
+ final int saveCount = canvas.save();
+ canvas.translate((width - mid.getWidth()) / 2, height + padding);
+ mid.draw(canvas);
+ canvas.restoreToCount(saveCount);
}
- final Layout end = mLayoutEnd;
+ final Layout end = mLabelEnd;
if (end != null) {
- canvas.save();
+ final int saveCount = canvas.save();
canvas.translate(width - end.getWidth(), height + padding);
end.draw(canvas);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
}
}
- private Layout makeLayout(CharSequence text) {
+ private Layout makeLabel(CharSequence text) {
final Resources res = getResources();
final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
paint.density = res.getDisplayMetrics().density;
paint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
paint.setColor(mLabelColor);
- paint.setTextSize(
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, res.getDisplayMetrics()));
+ paint.setTextSize(mLabelSize);
return new StaticLayout(text, paint,
(int) Math.ceil(Layout.getDesiredWidth(text, paint)),
Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
}
-
}
diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java
index 6250a25..7aaba66 100644
--- a/src/com/android/settings/widget/ChartNetworkSeriesView.java
+++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java
@@ -60,17 +60,17 @@ public class ChartNetworkSeriesView extends View {
private Path mPathFill;
private Path mPathEstimate;
+ private int mSafeRegion;
+
private long mStart;
private long mEnd;
- private long mPrimaryLeft;
- private long mPrimaryRight;
-
/** Series will be extended to reach this end time. */
private long mEndTime = Long.MIN_VALUE;
private boolean mPathValid = false;
private boolean mEstimateVisible = false;
+ private boolean mSecondary = false;
private long mMax;
private long mMaxEstimate;
@@ -93,8 +93,11 @@ public class ChartNetworkSeriesView extends View {
final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED);
final int fillSecondary = a.getColor(
R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED);
+ final int safeRegion = a.getDimensionPixelSize(
+ R.styleable.ChartNetworkSeriesView_safeRegion, 0);
setChartColor(stroke, fill, fillSecondary);
+ setSafeRegion(safeRegion);
setWillNotDraw(false);
a.recycle();
@@ -134,6 +137,10 @@ public class ChartNetworkSeriesView extends View {
mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1));
}
+ public void setSafeRegion(int safeRegion) {
+ mSafeRegion = safeRegion;
+ }
+
public void bindNetworkStats(NetworkStatsHistory stats) {
mStats = stats;
invalidatePath();
@@ -145,14 +152,8 @@ public class ChartNetworkSeriesView extends View {
mEnd = end;
}
- /**
- * Set the range to paint with {@link #mPaintFill}, leaving the remaining
- * area to be painted with {@link #mPaintFillSecondary}.
- */
- public void setPrimaryRange(long left, long right) {
- mPrimaryLeft = left;
- mPrimaryRight = right;
- invalidate();
+ public void setSecondary(boolean secondary) {
+ mSecondary = secondary;
}
public void invalidatePath() {
@@ -322,9 +323,6 @@ public class ChartNetworkSeriesView extends View {
generatePath();
}
- final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft);
- final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight);
-
if (mEstimateVisible) {
save = canvas.save();
canvas.clipRect(0, 0, getWidth(), getHeight());
@@ -332,21 +330,11 @@ public class ChartNetworkSeriesView extends View {
canvas.restoreToCount(save);
}
- save = canvas.save();
- canvas.clipRect(0, 0, primaryLeftPoint, getHeight());
- canvas.drawPath(mPathFill, mPaintFillSecondary);
- canvas.restoreToCount(save);
+ final Paint paintFill = mSecondary ? mPaintFillSecondary : mPaintFill;
save = canvas.save();
- canvas.clipRect(primaryRightPoint, 0, getWidth(), getHeight());
- canvas.drawPath(mPathFill, mPaintFillSecondary);
+ canvas.clipRect(mSafeRegion, 0, getWidth(), getHeight() - mSafeRegion);
+ canvas.drawPath(mPathFill, paintFill);
canvas.restoreToCount(save);
-
- save = canvas.save();
- canvas.clipRect(primaryLeftPoint, 0, primaryRightPoint, getHeight());
- canvas.drawPath(mPathFill, mPaintFill);
- canvas.drawPath(mPathStroke, mPaintStroke);
- canvas.restoreToCount(save);
-
}
}
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index 774e5d8..04fc862 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -58,6 +58,7 @@ public class ChartSweepView extends View {
private Rect mMargins = new Rect();
private float mNeighborMargin;
+ private int mSafeRegion;
private int mFollowAxis;
@@ -125,6 +126,7 @@ public class ChartSweepView extends View {
setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable));
setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1));
setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0));
+ setSafeRegion(a.getDimensionPixelSize(R.styleable.ChartSweepView_safeRegion, 0));
setLabelMinSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0));
setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0));
@@ -259,7 +261,6 @@ public class ChartSweepView extends View {
paint.density = getResources().getDisplayMetrics().density;
paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
paint.setColor(mLabelColor);
- paint.setShadowLayer(4 * paint.density, 0, 0, Color.BLACK);
mLabelTemplate = new SpannableStringBuilder(template);
mLabelLayout = new DynamicLayout(
@@ -383,6 +384,10 @@ public class ChartSweepView extends View {
mNeighborMargin = neighborMargin;
}
+ public void setSafeRegion(int safeRegion) {
+ mSafeRegion = safeRegion;
+ }
+
/**
* Set valid range this sweep can move within, defined by the given
* {@link ChartSweepView}. The most restrictive combination of all valid
@@ -709,7 +714,7 @@ public class ChartSweepView extends View {
mLabelLayout.draw(canvas);
}
canvas.restoreToCount(count);
- labelSize = (int) mLabelSize;
+ labelSize = (int) mLabelSize + mSafeRegion;
} else {
labelSize = 0;
}
diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java
index 69e6e94..30284bc 100644
--- a/src/com/android/settings/widget/ChartView.java
+++ b/src/com/android/settings/widget/ChartView.java
@@ -112,12 +112,18 @@ public class ChartView extends FrameLayout {
parentRect.set(mContent);
- if (child instanceof ChartNetworkSeriesView || child instanceof ChartGridView) {
+ if (child instanceof ChartNetworkSeriesView) {
// series are always laid out to fill entire graph area
// TODO: handle scrolling for series larger than content area
Gravity.apply(params.gravity, width, height, parentRect, childRect);
child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
+ } else if (child instanceof ChartGridView) {
+ // Grid uses some extra room for labels
+ Gravity.apply(params.gravity, width, height, parentRect, childRect);
+ child.layout(childRect.left, childRect.top, childRect.right,
+ childRect.bottom + child.getPaddingBottom());
+
} else if (child instanceof ChartSweepView) {
layoutSweep((ChartSweepView) child, parentRect, childRect);
child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
@@ -154,5 +160,4 @@ public class ChartView extends FrameLayout {
parentRect, childRect);
}
}
-
}
diff --git a/src/com/android/settings/widget/PieChartView.java b/src/com/android/settings/widget/PieChartView.java
deleted file mode 100644
index 6070190..0000000
--- a/src/com/android/settings/widget/PieChartView.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2011 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.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
-import android.graphics.RadialGradient;
-import android.graphics.RectF;
-import android.graphics.Shader.TileMode;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * Pie chart with multiple items.
- */
-public class PieChartView extends View {
- public static final String TAG = "PieChartView";
- public static final boolean LOGD = false;
-
- private static final boolean FILL_GRADIENT = false;
-
- private ArrayList<Slice> mSlices = Lists.newArrayList();
-
- private int mOriginAngle;
- private Matrix mMatrix = new Matrix();
-
- private Paint mPaintOutline = new Paint();
-
- private Path mPathSide = new Path();
- private Path mPathSideOutline = new Path();
-
- private Path mPathOutline = new Path();
-
- private int mSideWidth;
-
- public class Slice {
- public long value;
-
- public Path path = new Path();
- public Path pathSide = new Path();
- public Path pathOutline = new Path();
-
- public Paint paint;
-
- public Slice(long value, int color) {
- this.value = value;
- this.paint = buildFillPaint(color, getResources());
- }
- }
-
- public PieChartView(Context context) {
- this(context, null);
- }
-
- public PieChartView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PieChartView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- mPaintOutline.setColor(Color.BLACK);
- mPaintOutline.setStyle(Style.STROKE);
- mPaintOutline.setStrokeWidth(3f * getResources().getDisplayMetrics().density);
- mPaintOutline.setAntiAlias(true);
-
- mSideWidth = (int) (20 * getResources().getDisplayMetrics().density);
-
- setWillNotDraw(false);
- }
-
- private static Paint buildFillPaint(int color, Resources res) {
- final Paint paint = new Paint();
-
- paint.setColor(color);
- paint.setStyle(Style.FILL_AND_STROKE);
- paint.setAntiAlias(true);
-
- if (FILL_GRADIENT) {
- final int width = (int) (280 * res.getDisplayMetrics().density);
- paint.setShader(new RadialGradient(0, 0, width, color, darken(color), TileMode.MIRROR));
- }
-
- return paint;
- }
-
- public void setOriginAngle(int originAngle) {
- mOriginAngle = originAngle;
- }
-
- public void addSlice(long value, int color) {
- mSlices.add(new Slice(value, color));
- }
-
- public void removeAllSlices() {
- mSlices.clear();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final float centerX = getWidth() / 2;
- final float centerY = getHeight() / 2;
-
- mMatrix.reset();
- mMatrix.postScale(0.665f, 0.95f, centerX, centerY);
- mMatrix.postRotate(-40, centerX, centerY);
-
- generatePath();
- }
-
- public void generatePath() {
- if (LOGD) Log.d(TAG, "generatePath()");
-
- long total = 0;
- for (Slice slice : mSlices) {
- slice.path.reset();
- slice.pathSide.reset();
- slice.pathOutline.reset();
- total += slice.value;
- }
-
- mPathSide.reset();
- mPathSideOutline.reset();
- mPathOutline.reset();
-
- // bail when not enough stats to render
- if (total == 0) {
- invalidate();
- return;
- }
-
- final int width = getWidth();
- final int height = getHeight();
-
- final RectF rect = new RectF(0, 0, width, height);
- final RectF rectSide = new RectF();
- rectSide.set(rect);
- rectSide.offset(-mSideWidth, 0);
-
- mPathSide.addOval(rectSide, Direction.CW);
- mPathSideOutline.addOval(rectSide, Direction.CW);
- mPathOutline.addOval(rect, Direction.CW);
-
- int startAngle = mOriginAngle;
- for (Slice slice : mSlices) {
- final int sweepAngle = (int) (slice.value * 360 / total);
- final int endAngle = startAngle + sweepAngle;
-
- final float startAngleMod = startAngle % 360;
- final float endAngleMod = endAngle % 360;
- final boolean startSideVisible = startAngleMod > 90 && startAngleMod < 270;
- final boolean endSideVisible = endAngleMod > 90 && endAngleMod < 270;
-
- // draw slice
- slice.path.moveTo(rect.centerX(), rect.centerY());
- slice.path.arcTo(rect, startAngle, sweepAngle);
- slice.path.lineTo(rect.centerX(), rect.centerY());
-
- if (startSideVisible || endSideVisible) {
-
- // when start is beyond horizon, push until visible
- final float startAngleSide = startSideVisible ? startAngle : 450;
- final float endAngleSide = endSideVisible ? endAngle : 270;
- final float sweepAngleSide = endAngleSide - startAngleSide;
-
- // draw slice side
- slice.pathSide.moveTo(rect.centerX(), rect.centerY());
- slice.pathSide.arcTo(rect, startAngleSide, 0);
- slice.pathSide.rLineTo(-mSideWidth, 0);
- slice.pathSide.arcTo(rectSide, startAngleSide, sweepAngleSide);
- slice.pathSide.rLineTo(mSideWidth, 0);
- slice.pathSide.arcTo(rect, endAngleSide, -sweepAngleSide);
- }
-
- // draw slice outline
- slice.pathOutline.moveTo(rect.centerX(), rect.centerY());
- slice.pathOutline.arcTo(rect, startAngle, 0);
- if (startSideVisible) {
- slice.pathOutline.rLineTo(-mSideWidth, 0);
- }
- slice.pathOutline.moveTo(rect.centerX(), rect.centerY());
- slice.pathOutline.arcTo(rect, startAngle + sweepAngle, 0);
- if (endSideVisible) {
- slice.pathOutline.rLineTo(-mSideWidth, 0);
- }
-
- startAngle += sweepAngle;
- }
-
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
-
- canvas.concat(mMatrix);
-
- for (Slice slice : mSlices) {
- canvas.drawPath(slice.pathSide, slice.paint);
- }
- canvas.drawPath(mPathSideOutline, mPaintOutline);
-
- for (Slice slice : mSlices) {
- canvas.drawPath(slice.path, slice.paint);
- canvas.drawPath(slice.pathOutline, mPaintOutline);
- }
- canvas.drawPath(mPathOutline, mPaintOutline);
- }
-
- public static int darken(int color) {
- float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
- hsv[2] /= 2;
- hsv[1] /= 2;
- return Color.HSVToColor(hsv);
- }
-
-}