OSDN Git Service

CMFileManager: add usage stats by mime type
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / ui / widgets / DiskUsageGraph.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.cyanogenmod.filemanager.ui.widgets;
18
19 import android.content.Context;
20 import android.graphics.*;
21 import android.util.AttributeSet;
22 import android.view.View;
23 import android.widget.Toast;
24 import com.cyanogenmod.filemanager.R;
25 import com.cyanogenmod.filemanager.model.DiskUsage;
26 import com.cyanogenmod.filemanager.model.DiskUsageCategory;
27 import com.cyanogenmod.filemanager.ui.ThemeManager;
28 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.Future;
36
37 /**
38  * A class that display graphically the usage of a mount point.
39  */
40 public class DiskUsageGraph extends View {
41
42     /**
43      * This is a list for accessing the loaded colors
44      */
45     public static final List<Integer> COLOR_LIST = new ArrayList<Integer>();
46     /**
47      * This is an internal color id reference
48      */
49     private static final List<Integer> INTERNAL_COLOR_LIST = new ArrayList<Integer>() {
50         {
51
52             // Material Blue
53             add(R.color.material_palette_blue_1);
54             add(R.color.material_palette_blue_2);
55             add(R.color.material_palette_blue_3);
56             add(R.color.material_palette_blue_4);
57
58             // Material Lime
59             add(R.color.material_palette_green_1);
60             add(R.color.material_palette_green_2);
61             add(R.color.material_palette_green_3);
62             add(R.color.material_palette_green_4);
63
64             // Material Orange
65             add(R.color.material_palette_orange_1);
66             add(R.color.material_palette_orange_2);
67             add(R.color.material_palette_orange_3);
68             add(R.color.material_palette_orange_4);
69
70             // Material Pink
71             add(R.color.material_palette_pink_1);
72             add(R.color.material_palette_pink_2);
73             add(R.color.material_palette_pink_3);
74             add(R.color.material_palette_pink_4);
75
76
77         }
78     };
79
80     /**
81      * Initialize the color assets into memory for direct access
82      */
83     private void initializeColors() {
84         // Only load the colors if needed
85         if (COLOR_LIST.size() == 0) {
86             for (int colorId : INTERNAL_COLOR_LIST) {
87                 COLOR_LIST.add(getContext().getResources().getColor(colorId));
88             }
89         }
90     }
91
92     /**
93      * @hide
94      */
95     int mDiskWarningAngle = (360 * 95) / 100;
96
97     private static String sWarningText;
98
99     /**
100      * @hide
101      */
102     final List<DrawingObject> mDrawingObjects =
103             Collections.synchronizedList(new ArrayList<DiskUsageGraph.DrawingObject>(2));
104
105     /**
106      * @hide
107      * drawing objects lock
108      */
109     static final int[] LOCK = new int[0];
110
111     /**
112      * Constructor of <code>DiskUsageGraph</code>.
113      *
114      * @param context The current context
115      */
116     public DiskUsageGraph(Context context) {
117         this(context, null);
118     }
119
120     /**
121      * Constructor of <code>DiskUsageGraph</code>.
122      *
123      * @param context The current context
124      * @param attrs The attributes of the XML tag that is inflating the view.
125      */
126     public DiskUsageGraph(Context context, AttributeSet attrs) {
127         this(context, attrs, 0);
128     }
129
130     /**
131      * Constructor of <code>DiskUsageGraph</code>.
132      *
133      * @param context The current context
134      * @param attrs The attributes of the XML tag that is inflating the view.
135      * @param defStyle The default style to apply to this view. If 0, no style
136      *        will be applied (beyond what is included in the theme). This may
137      *        either be an attribute resource, whose value will be retrieved
138      *        from the current theme, or an explicit style resource.
139      */
140     public DiskUsageGraph(Context context, AttributeSet attrs, int defStyle) {
141         super(context, attrs, defStyle);
142         initializeColors();
143         if (sWarningText == null) {
144             sWarningText = context.getResources().getString(R.string.pref_disk_usage_warning_level);
145         }
146     }
147
148     /**
149      * {@inheritDoc}
150      */
151     @Override
152     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
153         int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
154         int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
155         int size = Math.min(parentWidth, parentHeight);
156         this.setMeasuredDimension(size, size);
157     }
158
159     /**
160      * Method that sets the free disk space percentage after the widget change his color to advise
161      * the user
162      *
163      * @param percentage The free disk space percentage
164      */
165     public void setFreeDiskSpaceWarningLevel(int percentage) {
166         this.mDiskWarningAngle = (360 * percentage) / 100;
167     }
168
169     // Handle thread for drawing calculations
170     private Future mAnimationFuture = null;
171     private static ExecutorService sThreadPool = Executors.newFixedThreadPool(1);
172
173     /**
174      * Method that draw the disk usage.
175      *
176      * @param diskUsage {@link com.cyanogenmod.filemanager.model.DiskUsage} The disk usage params
177      */
178     public void drawDiskUsage(DiskUsage diskUsage) {
179
180         // Clear if a current drawing exit
181         if (mAnimationFuture != null && !mAnimationFuture.isCancelled()) {
182             mAnimationFuture.cancel(true);
183         }
184
185         // Clear canvas
186         synchronized (LOCK) {
187             this.mDrawingObjects.clear();
188         }
189         invalidate();
190
191         // Start drawing thread
192         AnimationDrawingRunnable animationDrawingRunnable = new AnimationDrawingRunnable(diskUsage);
193         mAnimationFuture = sThreadPool.submit(animationDrawingRunnable);
194
195     }
196
197     /**
198      * {@inheritDoc}
199      */
200     @Override
201     protected void onDraw(Canvas canvas) {
202         //Draw super surface
203         super.onDraw(canvas);
204
205         //Draw all the drawing objects
206         synchronized (LOCK) {
207             for (DrawingObject dwo : this.mDrawingObjects) {
208                 canvas.drawArc(dwo.mRectF, dwo.mStartAngle, dwo.mSweepAngle, false, dwo.mPaint);
209             }
210         }
211     }
212
213     /**
214      * A thread for drawing the animation of the graph.
215      */
216     private class AnimationDrawingRunnable implements Runnable {
217
218         private final DiskUsage mDiskUsage;
219
220         // Delay in between UI updates and slow down calculations
221         private static final long ANIMATION_DELAY = 1l;
222
223         // Slop space adjustment for space between segments
224         private static final int SLOP = 2;
225
226         // flags
227         private static final boolean USE_COLORS = true;
228
229         /**
230          * Constructor of <code>AnimationDrawingThread</code>.
231          *
232          * @param diskUsage The disk usage
233          */
234         public AnimationDrawingRunnable(DiskUsage diskUsage) {
235             this.mDiskUsage = diskUsage;
236         }
237
238         private void sleepyTime() {
239             try {
240                 Thread.sleep(ANIMATION_DELAY);
241             } catch (InterruptedException ignored) {
242             }
243         }
244
245         private void redrawCanvas() {
246             //Redraw the canvas
247             post(new Runnable() {
248                 @Override
249                 public void run() {
250                     invalidate();
251                 }
252             });
253         }
254
255         private void drawTotal(Rect rect, int stroke) {
256             // Draw total
257             DrawingObject drawingObject = createDrawingObject(rect, "disk_usage_total_color",
258                     stroke);
259             synchronized (LOCK) {
260                 mDrawingObjects.add(drawingObject);
261             }
262             while (drawingObject.mSweepAngle < 360) {
263                 drawingObject.mSweepAngle++;
264                 redrawCanvas();
265                 sleepyTime();
266             }
267         }
268
269         private void drawUsed(Rect rect, int stroke, float used) {
270             // Draw used
271             DrawingObject drawingObject = createDrawingObject(rect, "disk_usage_used_color", stroke);
272             synchronized (LOCK) {
273                 mDrawingObjects.add(drawingObject);
274             }
275             while (drawingObject.mSweepAngle < used) {
276                 drawingObject.mSweepAngle++;
277                 redrawCanvas();
278                 sleepyTime();
279             }
280         }
281
282         private void drawUsedWithColors(Rect rect, int stroke) {
283             // Draw used segments
284             if (mDiskUsage != null) {
285                 int lastSweepAngle = 0;
286                 float catUsed = 100.0f;
287                 int color;
288                 int index = 0;
289                 for (DiskUsageCategory category : mDiskUsage.getUsageCategoryList()) {
290                     catUsed = (category.getSizeBytes() * 100) / mDiskUsage.getTotal(); // calc percent
291                     catUsed = (catUsed < 1) ? 1 : catUsed; // Normalize
292                     catUsed = (360 * catUsed) / 100; // calc angle
293
294                     // Figure out a color
295                     if (index > -1 && index < COLOR_LIST.size()) {
296                         color = COLOR_LIST.get(index);
297                         index++;
298                     } else {
299                         index = 0;
300                         color = COLOR_LIST.get(index);
301                     }
302
303                     DrawingObject drawingObject = createDrawingObjectNoTheme(rect, color, stroke);
304                      drawingObject.mStartAngle += lastSweepAngle;
305                     synchronized (LOCK) {
306                         mDrawingObjects.add(drawingObject);
307                     }
308                     while (drawingObject.mSweepAngle < catUsed + SLOP) {
309                         drawingObject.mSweepAngle++;
310                         redrawCanvas();
311                         sleepyTime();
312                     }
313                     lastSweepAngle += drawingObject.mSweepAngle - SLOP;
314                 }
315             }
316         }
317
318         /**
319          * {@inheritDoc}
320          */
321         @Override
322         public void run() {
323             //Get information about the drawing zone, and adjust the size
324             Rect rect = new Rect();
325             getDrawingRect(rect);
326             int stroke = (rect.width() / 2) / 2;
327             rect.left += stroke / 2;
328             rect.right -= stroke / 2;
329             rect.top += stroke / 2;
330             rect.bottom -= stroke / 2;
331
332             float used = 100.0f;
333             if (this.mDiskUsage != null && this.mDiskUsage.getTotal() != 0) {
334                 used = (this.mDiskUsage.getUsed() * 100) / this.mDiskUsage.getTotal();
335             }
336             //Translate to angle
337             used = (360 * used) / 100;
338
339             // Draws out the graph background color
340             drawTotal(rect, stroke);
341
342             // Draw the usage
343             if (USE_COLORS) {
344                 drawUsedWithColors(rect, stroke);
345             } else {
346                 drawUsed(rect, stroke, used);
347             }
348
349             if (used >= mDiskWarningAngle) {
350                 Toast.makeText(getContext(), sWarningText, Toast.LENGTH_SHORT).show();
351             }
352
353         }
354
355         /**
356          * Method that creates the drawing object.
357          *
358          * @param rect The area of drawing
359          * @param colorResourceThemeId The theme resource identifier of the color
360          * @param stroke The stroke width
361          *
362          * @return DrawingObject The drawing object
363          */
364         private DrawingObject createDrawingObject(
365                 Rect rect, String colorResourceThemeId, int stroke) {
366             DrawingObject out = new DrawingObject();
367             out.mSweepAngle = 0;
368             Theme theme = ThemeManager.getCurrentTheme(getContext());
369             out.mPaint.setColor(theme.getColor(getContext(), colorResourceThemeId));
370             out.mPaint.setStrokeWidth(stroke);
371             out.mPaint.setAntiAlias(true);
372             out.mPaint.setStrokeCap(Paint.Cap.BUTT);
373             out.mPaint.setStyle(Paint.Style.STROKE);
374             out.mRectF = new RectF(rect);
375             return out;
376         }
377
378         /**
379          * Method that creates the drawing object.
380          *
381          * @param rect The area of drawing
382          * @param color Integer id of the color
383          * @param stroke The stroke width
384          *
385          * @return DrawingObject The drawing object
386          *
387          * [TODO][MSB]: Implement colors for sections into theme
388          */
389         @Deprecated
390         private DrawingObject createDrawingObjectNoTheme(
391                 Rect rect, int color, int stroke) {
392             DrawingObject out = new DrawingObject();
393             out.mSweepAngle = 0;
394             out.mPaint.setColor(color);
395             out.mPaint.setStrokeWidth(stroke);
396             out.mPaint.setAntiAlias(true);
397             out.mPaint.setStrokeCap(Paint.Cap.BUTT);
398             out.mPaint.setStyle(Paint.Style.STROKE);
399             out.mRectF = new RectF(rect);
400             return out;
401         }
402     }
403
404     /**
405      * A class with information about a drawing object.
406      */
407     private class DrawingObject {
408         DrawingObject() {/**NON BLOCK**/}
409
410         int mStartAngle = -180;
411         int mSweepAngle = 0;
412         Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
413         RectF mRectF = new RectF();
414     }
415
416 }