• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source 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 package com.android.settings.widget;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.graphics.Canvas;
21 import android.graphics.ColorFilter;
22 import android.graphics.Paint;
23 import android.graphics.PorterDuff;
24 import android.graphics.PorterDuffColorFilter;
25 import android.text.TextPaint;
26 import android.util.AttributeSet;
27 import android.view.View;
28 
29 import com.android.settings.R;
30 import com.android.settings.Utils;
31 
32 /**
33  * DonutView represents a donut graph. It visualizes a certain percentage of fullness with a
34  * corresponding label with the fullness on the inside (i.e. "50%" inside of the donut).
35  */
36 public class DonutView extends View {
37     private static final int TOP = -90;
38     // From manual testing, this is the longest we can go without visual errors.
39     private static final int LINE_CHARACTER_LIMIT = 10;
40     private float mStrokeWidth;
41     private float mDeviceDensity;
42     private int mPercent;
43     private Paint mBackgroundCircle;
44     private Paint mFilledArc;
45     private TextPaint mTextPaint;
46     private TextPaint mBigNumberPaint;
47     private String mPercentString;
48     private String mFullString;
49 
DonutView(Context context)50     public DonutView(Context context) {
51         super(context);
52     }
53 
DonutView(Context context, AttributeSet attrs)54     public DonutView(Context context, AttributeSet attrs) {
55         super(context, attrs);
56         mDeviceDensity = getResources().getDisplayMetrics().density;
57         mStrokeWidth = 6f * mDeviceDensity;
58         final ColorFilter mAccentColorFilter =
59                 new PorterDuffColorFilter(
60                         Utils.getColorAttr(context, android.R.attr.colorAccent),
61                         PorterDuff.Mode.SRC_IN);
62 
63         mBackgroundCircle = new Paint();
64         mBackgroundCircle.setAntiAlias(true);
65         mBackgroundCircle.setStrokeCap(Paint.Cap.BUTT);
66         mBackgroundCircle.setStyle(Paint.Style.STROKE);
67         mBackgroundCircle.setStrokeWidth(mStrokeWidth);
68         mBackgroundCircle.setColorFilter(mAccentColorFilter);
69         mBackgroundCircle.setColor(context.getColor(R.color.meter_background_color));
70 
71         mFilledArc = new Paint();
72         mFilledArc.setAntiAlias(true);
73         mFilledArc.setStrokeCap(Paint.Cap.BUTT);
74         mFilledArc.setStyle(Paint.Style.STROKE);
75         mFilledArc.setStrokeWidth(mStrokeWidth);
76         mFilledArc.setColor(Utils.getDefaultColor(mContext, R.color.meter_consumed_color));
77         mFilledArc.setColorFilter(mAccentColorFilter);
78 
79         Resources resources = context.getResources();
80         mTextPaint = new TextPaint();
81         mTextPaint.setColor(Utils.getColorAccent(getContext()));
82         mTextPaint.setAntiAlias(true);
83         mTextPaint.setTextSize(
84                 resources.getDimension(R.dimen.storage_donut_view_label_text_size));
85         mTextPaint.setTextAlign(Paint.Align.CENTER);
86 
87         mBigNumberPaint = new TextPaint();
88         mBigNumberPaint.setColor(Utils.getColorAccent(getContext()));
89         mBigNumberPaint.setAntiAlias(true);
90         mBigNumberPaint.setTextSize(
91                 resources.getDimension(R.dimen.storage_donut_view_percent_text_size));
92         mBigNumberPaint.setTextAlign(Paint.Align.CENTER);
93     }
94 
95     @Override
onDraw(Canvas canvas)96     protected void onDraw(Canvas canvas) {
97         super.onDraw(canvas);
98         drawDonut(canvas);
99         drawInnerText(canvas);
100     }
101 
drawDonut(Canvas canvas)102     private void drawDonut(Canvas canvas) {
103         canvas.drawArc(
104                 0 + mStrokeWidth,
105                 0 + mStrokeWidth,
106                 getWidth() - mStrokeWidth,
107                 getHeight() - mStrokeWidth,
108                 TOP,
109                 360,
110                 false,
111                 mBackgroundCircle);
112 
113         canvas.drawArc(
114                 0 + mStrokeWidth,
115                 0 + mStrokeWidth,
116                 getWidth() - mStrokeWidth,
117                 getHeight() - mStrokeWidth,
118                 TOP,
119                 (360 * mPercent / 100),
120                 false,
121                 mFilledArc);
122     }
123 
drawInnerText(Canvas canvas)124     private void drawInnerText(Canvas canvas) {
125         final float centerX = getWidth() / 2;
126         final float centerY = getHeight() / 2;
127         final float totalHeight = getTextHeight(mTextPaint) + getTextHeight(mBigNumberPaint);
128         final float startY = centerY + totalHeight / 2;
129 
130         // The first line is the height of the bottom text + its descender above the bottom line.
131         canvas.drawText(mPercentString, centerX,
132                 startY - getTextHeight(mTextPaint) - mBigNumberPaint.descent(),
133                 mBigNumberPaint);
134         // The second line starts at the bottom + room for the descender.
135         canvas.drawText(mFullString, centerX, startY - mTextPaint.descent(), mTextPaint);
136     }
137 
138     /**
139      * Set a percentage full to have the donut graph.
140      */
setPercentage(int percent)141     public void setPercentage(int percent) {
142         mPercent = percent;
143         mPercentString = Utils.formatPercentage(mPercent);
144         mFullString = getContext().getString(R.string.storage_percent_full);
145         if (mFullString.length() > LINE_CHARACTER_LIMIT) {
146             mTextPaint.setTextSize(
147                     getContext()
148                             .getResources()
149                             .getDimension(
150                                     R.dimen.storage_donut_view_shrunken_label_text_size));
151         }
152         invalidate();
153     }
154 
getTextHeight(TextPaint paint)155     private float getTextHeight(TextPaint paint) {
156         // Technically, this should be the cap height, but I can live with the descent - ascent.
157         return paint.descent() - paint.ascent();
158     }
159 }
160