package org.ovirt.mobile.movirt.ui.dashboard; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.os.Looper; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import org.ovirt.mobile.movirt.R; import org.ovirt.mobile.movirt.util.usage.MemorySize; import org.ovirt.mobile.movirt.util.usage.Percentage; import org.ovirt.mobile.movirt.util.usage.UsageResource; public class PercentageCircleView extends View { private static final double FOREGROUND_COLOR_A_MAX_PERCENTAGE = 0.5f; private static final double FOREGROUND_COLOR_B_MAX_PERCENTAGE = 0.75f; private static final double FOREGROUND_COLOR_C_MAX_PERCENTAGE = 1.0f; private static final int MAX_ANGLE = 360; //attr private int wholeBackgroundColor = Color.parseColor("#ffffff"); private int backgroundColor = Color.parseColor("#666666"); private int foregroundColorA = Color.parseColor("#3f9c35");//when percentage < FOREGROUND_COLOR_A_MAX_PERCENTAGE private int foregroundColorB = Color.parseColor("#ec7a08");//when percentage >= FOREGROUND_COLOR_A_MAX_PERCENTAGE && < FOREGROUND_COLOR_B_MAX_PERCENTAGE private int foregroundColorC = Color.parseColor("#cc0000");//when percentage >= FOREGROUND_COLOR_C_MAX_PERCENTAGE private int textColor; private int strokeWidth = 10;// default stroke width private int startAngle = -90;// default 12 o'clock private boolean needShowText = true;// default show text private Context context; private RectF wholeRectF, foregroundRectF, backgroundRectF; private Paint wholeBackgroundPaint; private Paint backgroundPaint; private Paint foregroundPaint; private Paint textPaint; private int currentForegroundColor = foregroundColorA; private int angleStep = 0; private int minWidth; private UsageResource maxResource = new MemorySize(); private UsageResource usedResource = new MemorySize(); private String usedResourceDescription = ""; public PercentageCircleView(Context context) { super(context); this.context = context; init(); } public PercentageCircleView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; parseAttributes(attrs); init(); } private void parseAttributes(AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.percentage_circle_view); wholeBackgroundColor = a.getColor(R.styleable.percentage_circle_view_whole_background_color, wholeBackgroundColor); foregroundColorA = a.getColor(R.styleable.percentage_circle_view_foreground_color_a, foregroundColorA); foregroundColorB = a.getColor(R.styleable.percentage_circle_view_foreground_color_b, foregroundColorB); foregroundColorC = a.getColor(R.styleable.percentage_circle_view_foreground_color_c, foregroundColorC); backgroundColor = a.getColor(R.styleable.percentage_circle_view_background_color, backgroundColor); textColor = a.getColor(R.styleable.percentage_circle_view_text_color, textColor); strokeWidth = a.getInt(R.styleable.percentage_circle_view_stroke_width, strokeWidth); startAngle = a.getInt(R.styleable.percentage_circle_view_start_angle, startAngle); needShowText = a.getBoolean(R.styleable.percentage_circle_view_need_show_text, needShowText); textColor = textColor == 0 ? currentForegroundColor : textColor; a.recycle(); } private void init() { initWholeBackgroundPaint(); initBackgroundPaint(); initForegroundPaint(); initTextPaint(); initRectF(); } private void initWholeBackgroundPaint() { wholeBackgroundPaint = new Paint(); wholeBackgroundPaint.setAntiAlias(true); wholeBackgroundPaint.setColor(wholeBackgroundColor); } private void initBackgroundPaint() { backgroundPaint = new Paint(); backgroundPaint.setAntiAlias(true); backgroundPaint.setStyle(Paint.Style.STROKE); backgroundPaint.setStrokeWidth(strokeWidth); backgroundPaint.setColor(backgroundColor); } private void initForegroundPaint() { foregroundPaint = new Paint(); foregroundPaint.setAntiAlias(true); foregroundPaint.setDither(true); foregroundPaint.setStyle(Paint.Style.STROKE); foregroundPaint.setStrokeCap(Paint.Cap.ROUND); foregroundPaint.setStrokeWidth(strokeWidth); foregroundPaint.setColor(currentForegroundColor); } private void initTextPaint() { textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(textColor); } private void initRectF() { backgroundRectF = new RectF(); foregroundRectF = new RectF(); wholeRectF = new RectF(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int size = 0; int width = getMeasuredWidth(); int height = getMeasuredHeight(); int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight(); int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom(); if (widthWithoutPadding > heigthWithoutPadding) { size = heigthWithoutPadding; } else { size = widthWithoutPadding; } setMeasuredDimension(size + getPaddingLeft() + getPaddingRight(), size + getPaddingTop() + getPaddingBottom()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { minWidth = Math.min(getWidth(), getHeight()); int rLeft = strokeWidth / 2; int rTop = strokeWidth / 2; int rRight = minWidth - strokeWidth / 2; int rBottom = minWidth - strokeWidth / 2; backgroundRectF.set(rLeft, rTop, rRight, rBottom); foregroundRectF.set(rLeft, rTop, rRight, rBottom); wholeRectF.set(strokeWidth / 2, strokeWidth / 2, minWidth - strokeWidth / 2, minWidth - strokeWidth / 2); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawWholeBackground(canvas); drawBackground(canvas); drawForeground(canvas); drawTextAndSummary(canvas); } private void drawWholeBackground(Canvas canvas) { canvas.drawCircle(foregroundRectF.centerX(), foregroundRectF.centerY(), foregroundRectF.height() / 2, wholeBackgroundPaint); } @Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: if (isInCircle(x, y)) { setActivated(true); invalidate(); } break; case MotionEvent.ACTION_MOVE: if (!isInCircle(x, y)) { setActivated(false); invalidate(); } break; case MotionEvent.ACTION_UP: setActivated(false); invalidate(); break; } return true; } private boolean isInCircle(float x, float y) { float dx = foregroundRectF.centerX() - x; float dy = foregroundRectF.centerY() - y; return Math.pow(dx, 2) + Math.pow(dy, 2) <= Math.pow(foregroundRectF.height() / 2, 2); } private void drawTextAndSummary(Canvas canvas) { if (!needShowText) return; //draw progress text String text = ""; if (usedResource instanceof MemorySize) { text = ((MemorySize) usedResource).getReadableValueAsString(1, ((MemorySize) maxResource).getReadableUnit()); } else if (usedResource instanceof Percentage) { text = usedResource.toString(); } int textSize = (int) ((minWidth - strokeWidth * 2) / 3.5); textPaint.setTextSize(textSize); Paint.FontMetrics fm = textPaint.getFontMetrics(); float textHeight = (float) Math.ceil(fm.descent - fm.top); float textWidth = textPaint.measureText(text); float x = (minWidth - textWidth) / 2; float y = (minWidth - textHeight) / 2 + textSize; canvas.drawText(text, x, y, textPaint); //draw summary int summaryTextSize = (int) ((minWidth - strokeWidth * 2) / 11.0); textPaint.setTextSize(summaryTextSize); float summaryTextWidth = textPaint.measureText(usedResourceDescription); float summaryX = (minWidth - summaryTextWidth) / 2; float summaryY = y + summaryTextSize * 3 / 2; canvas.drawText(usedResourceDescription, summaryX, summaryY, textPaint); } private void drawForeground(Canvas canvas) { double resourcePercentageRatio = usedResource.getValue() / (double) (maxResource.getValue() == 0 ? 1 : maxResource.getValue()); int startAngle = angleStep + this.startAngle; int sweepAngle = (int) (resourcePercentageRatio * MAX_ANGLE); if (Double.compare(resourcePercentageRatio, FOREGROUND_COLOR_A_MAX_PERCENTAGE) < 0) { currentForegroundColor = foregroundColorA; } else if (Double.compare(resourcePercentageRatio, FOREGROUND_COLOR_B_MAX_PERCENTAGE) < 0) { currentForegroundColor = foregroundColorB; } else { currentForegroundColor = foregroundColorC; } foregroundPaint.setColor(adjustColor(currentForegroundColor)); canvas.drawArc(foregroundRectF, startAngle, sweepAngle, false, foregroundPaint); } private void drawBackground(Canvas canvas) { backgroundPaint.setColor(adjustColor(backgroundColor)); canvas.drawArc(backgroundRectF, startAngle, MAX_ANGLE, false, backgroundPaint); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); } /** * invalidate view */ public void invalidateUi() { if (Looper.getMainLooper() == Looper.myLooper()) { invalidate(); } else { postInvalidate(); } } public void setMaxResource(UsageResource maxResource) { if (maxResource != null) { this.maxResource = maxResource; } } public UsageResource getMaxResource() { return maxResource; } public void setUsedResource(UsageResource usedResource) { if (maxResource != null) { this.usedResource = usedResource; invalidateUi(); } } public UsageResource getUsedResource() { return usedResource; } public void setUsedResourceDescription(String usedResourceDescription) { if (usedResourceDescription != null) { this.usedResourceDescription = usedResourceDescription; } } public String getUsedResourceDescription() { return usedResourceDescription; } /** * Used for altering color in touch up/down events * * @param color color * @return adjusted color */ private int adjustColor(int color) { if (isActivated()) { final double valueShift = 0.07; float[] hsbVals = new float[3]; Color.colorToHSV(color, hsbVals); hsbVals[2] += valueShift; color = Color.HSVToColor(hsbVals); } return color; } }