/*
* Copyright (C) 2012 The CyanogenMod 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.cyanogenmod.filemanager.ui.widgets;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.cyanogenmod.filemanager.model.DiskUsage;
import com.cyanogenmod.filemanager.ui.ThemeManager;
import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A class that display graphically the usage of a mount point.
*/
public class DiskUsageGraph extends View {
/**
* @hide
*/
int mDiskWarningAngle = (360 * 95) / 100;
private AnimationDrawingThread mThread;
/**
* @hide
*/
final List<DrawingObject> mDrawingObjects =
Collections.synchronizedList(new ArrayList<DiskUsageGraph.DrawingObject>(2));
/**
* Constructor of <code>DiskUsageGraph</code>.
*
* @param context The current context
*/
public DiskUsageGraph(Context context) {
super(context);
}
/**
* Constructor of <code>DiskUsageGraph</code>.
*
* @param context The current context
* @param attrs The attributes of the XML tag that is inflating the view.
*/
public DiskUsageGraph(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Constructor of <code>DiskUsageGraph</code>.
*
* @param context The current context
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyle The default style to apply to this view. If 0, no style
* will be applied (beyond what is included in the theme). This may
* either be an attribute resource, whose value will be retrieved
* from the current theme, or an explicit style resource.
*/
public DiskUsageGraph(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* {@inheritDoc}
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
int size = Math.min(parentWidth, parentHeight);
this.setMeasuredDimension(size, size);
}
/**
* Method that sets the free disk space percentage after the widget change his color
* to advise the user
*
* @param percentage The free disk space percentage
*/
public void setFreeDiskSpaceWarningLevel(int percentage) {
this.mDiskWarningAngle = (360 * percentage) / 100;
}
/**
* Method that draw the disk usage.
*
* @param diskUsage The disk usage
*/
public void drawDiskUsage(DiskUsage diskUsage) {
// Clear if a current drawing exit
if (this.mThread != null) {
this.mThread.exit();
}
this.mDrawingObjects.clear();
invalidate();
// Start drawing thread
this.mThread = new AnimationDrawingThread(diskUsage);
this.mThread.start();
}
/**
* {@inheritDoc}
*/
@Override
protected void onDraw(Canvas canvas) {
//Draw super surface
super.onDraw(canvas);
//Draw all the drawing objects
int cc = this.mDrawingObjects.size();
for (int i = 0; i < cc; i++) {
DrawingObject dwo = this.mDrawingObjects.get(i);
canvas.drawArc(dwo.mRectF, dwo.mStartAngle, dwo.mSweepAngle, false, dwo.mPaint);
}
}
/**
* A thread for drawing the animation of the graph.
*/
private class AnimationDrawingThread extends Thread {
private final DiskUsage mDiskUsage;
private boolean mRunning;
private final Object mSync = new Object();
private int mIndex = 0;
/**
* Constructor of <code>AnimationDrawingThread</code>.
*
* @param diskUsage The disk usage
*/
public AnimationDrawingThread(DiskUsage diskUsage) {
super();
this.mDiskUsage = diskUsage;
this.mRunning = false;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("null")
public void run() {
//Get information about the drawing zone, and adjust the size
Rect rect = new Rect();
getDrawingRect(rect);
int stroke = (rect.width() / 2) / 2;
rect.left += stroke / 2;
rect.right -= stroke / 2;
rect.top += stroke / 2;
rect.bottom -= stroke / 2;
float used = 0.0f;
if (this.mDiskUsage == null) {
used = 100.0f;
} else if (this.mDiskUsage.getTotal() != 0) {
used = (this.mDiskUsage.getUsed() * 100) / this.mDiskUsage.getTotal();
}
//Translate to angle
used = (360 * used) / 100;
synchronized (this.mSync) {
this.mRunning = true;
}
try {
boolean disk_warning = false;
while (this.mRunning) {
//Get the current arc
DrawingObject dwo = null;
if (DiskUsageGraph.this.mDrawingObjects != null
&& DiskUsageGraph.this.mDrawingObjects.size() > this.mIndex) {
dwo = DiskUsageGraph.this.mDrawingObjects.get(this.mIndex);
}
//Draw the total arc circle and then the used arc circle
if (this.mIndex == 0 && dwo == null) {
//Initialize the total arc circle
DiskUsageGraph.this.mDrawingObjects.add(
createDrawingObject(
rect, "disk_usage_total_color", stroke)); //$NON-NLS-1$
continue;
}
if (this.mIndex == 1 && dwo == null) {
//Initialize the used arc circle
DiskUsageGraph.this.mDrawingObjects.add(
createDrawingObject(
rect, "disk_usage_used_color", stroke)); //$NON-NLS-1$
continue;
}
if (this.mIndex == 1 && !disk_warning &&
dwo.mSweepAngle >= DiskUsageGraph.this.mDiskWarningAngle) {
Theme theme = ThemeManager.getCurrentTheme(getContext());
dwo.mPaint.setColor(
theme.getColor(
getContext(),
"disk_usage_used_warning_color")); //$NON-NLS-1$
disk_warning = true;
}
//Redraw the canvas
post(new Runnable() {
@Override
public void run() {
invalidate();
}
});
//Next draw call
dwo.mSweepAngle++;
if (this.mIndex >= 1) {
//Only fill until used
if ((dwo.mSweepAngle >= used) || (this.mIndex > 1)) {
synchronized (this.mSync) {
break; //End of the animation
}
}
}
if (dwo.mSweepAngle == 360) {
this.mIndex++;
}
try {
Thread.sleep(1L);
} catch (Throwable ex) {
/**NON BLOCK**/
}
}
} finally {
try {
synchronized (this.mSync) {
this.mRunning = false;
this.mSync.notify();
}
} catch (Throwable ex) {
/**NON BLOCK**/
}
}
}
/**
* Method that force the thread to exit.
*/
public void exit() {
try {
synchronized (this.mSync) {
if (this.mRunning) {
this.mRunning = false;
this.mSync.wait();
}
}
} catch (Throwable ex) {
/**NON BLOCK**/
}
}
/**
* Method that creates the drawing object.
*
* @param rect The area of drawing
* @param colorResourceThemeId The theme resource identifier of the color
* @param stroke The stroke width
* @return DrawingObject The drawing object
*/
private DrawingObject createDrawingObject(
Rect rect, String colorResourceThemeId, int stroke) {
DrawingObject out = new DrawingObject();
out.mSweepAngle = 0;
Theme theme = ThemeManager.getCurrentTheme(getContext());
out.mPaint.setColor(theme.getColor(getContext(), colorResourceThemeId));
out.mPaint.setStrokeWidth(stroke);
out.mPaint.setAntiAlias(true);
out.mPaint.setStrokeCap(Paint.Cap.BUTT);
out.mPaint.setStyle(Paint.Style.STROKE);
out.mRectF = new RectF(rect);
return out;
}
}
/**
* A class with information about a drawing object.
*/
private class DrawingObject {
DrawingObject() {/**NON BLOCK**/}
int mStartAngle = -180;
int mSweepAngle = 0;
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
RectF mRectF = new RectF();
}
}