blob: aa4362e2df8237c16c5a56ae1336833cb533f08d [file] [log] [blame]
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -04001/*
2 * Copyright (C) 2013 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
17package com.android.systemui;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.Canvas;
26import android.graphics.Paint;
27import android.graphics.Rect;
28import android.graphics.RectF;
29import android.graphics.Typeface;
30import android.graphics.drawable.Drawable;
31import android.os.BatteryManager;
32import android.provider.Settings;
33import android.util.AttributeSet;
34import android.view.View;
35
36public class BatteryMeterView extends View {
37 public static final String TAG = BatteryMeterView.class.getSimpleName();
38 public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
39
40 public static final boolean ENABLE_PERCENT = true;
41 public static final boolean SINGLE_DIGIT_PERCENT = false;
42 public static final boolean SHOW_100_PERCENT = false;
43
44 public static final int FULL = 96;
45 public static final int EMPTY = 4;
46
47 int[] mColors;
48
49 boolean mShowPercent = true;
50 Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint;
51 int mButtonHeight;
52 private float mTextHeight, mWarningTextHeight;
53 Drawable mLightning;
54
55 private int mHeight;
56 private int mWidth;
57 private String mWarningString;
58
59 private class BatteryTracker extends BroadcastReceiver {
60 // current battery status
61 int level;
62 String percentStr;
63 int plugType;
64 boolean plugged;
65 int health;
66 int status;
67 String technology;
68 int voltage;
69 int temperature;
70 boolean testmode = false;
71
72 @Override
73 public void onReceive(Context context, Intent intent) {
74 final String action = intent.getAction();
75 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
76 if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
77
78 level = (int)(100f
79 * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
80 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
81
82 plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
83 plugged = plugType != 0;
84 health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
85 BatteryManager.BATTERY_HEALTH_UNKNOWN);
86 status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
87 BatteryManager.BATTERY_STATUS_UNKNOWN);
88 technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
89 voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
90 temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
91
92 setContentDescription(
93 context.getString(R.string.accessibility_battery_level, level));
94 postInvalidate();
95 } else if (action.equals(ACTION_LEVEL_TEST)) {
96 testmode = true;
97 post(new Runnable() {
98 int curLevel = 0;
99 int incr = 1;
100 int saveLevel = level;
101 int savePlugged = plugType;
102 Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
103 @Override
104 public void run() {
105 if (curLevel < 0) {
106 testmode = false;
107 dummy.putExtra("level", saveLevel);
108 dummy.putExtra("plugged", savePlugged);
109 dummy.putExtra("testmode", false);
110 } else {
111 dummy.putExtra("level", curLevel);
112 dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);
113 dummy.putExtra("testmode", true);
114 }
115 getContext().sendBroadcast(dummy);
116
117 if (!testmode) return;
118
119 curLevel += incr;
120 if (curLevel == 100) {
121 incr *= -1;
122 }
123 postDelayed(this, 200);
124 }
125 });
126 }
127 }
128 }
129
130 BatteryTracker mTracker = new BatteryTracker();
131
132 @Override
133 public void onAttachedToWindow() {
134 super.onAttachedToWindow();
135
136 IntentFilter filter = new IntentFilter();
137 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
138 filter.addAction(ACTION_LEVEL_TEST);
139 getContext().registerReceiver(mTracker, filter);
140 }
141
142 @Override
143 public void onDetachedFromWindow() {
144 super.onDetachedFromWindow();
145
146 getContext().unregisterReceiver(mTracker);
147 }
148
149 public BatteryMeterView(Context context) {
150 this(context, null, 0);
151 }
152
153 public BatteryMeterView(Context context, AttributeSet attrs) {
154 this(context, attrs, 0);
155 }
156
157 public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
158 super(context, attrs, defStyle);
159
160 final Resources res = context.getResources();
161 TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
162 TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
163
164 final int N = levels.length();
165 mColors = new int[2*N];
166 for (int i=0; i<N; i++) {
167 mColors[2*i] = levels.getInt(i, 0);
168 mColors[2*i+1] = colors.getColor(i, 0);
169 }
170
171 mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
172 context.getContentResolver(), "status_bar_show_battery_percent", 0);
173
174 mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
175
176 mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
177 mFramePaint.setColor(res.getColor(R.color.batterymeter_frame_color));
178 mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
179 mBatteryPaint.setColor(0xFF00FF00); // will be replaced by something from mColors
180
181 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
182 mTextPaint.setColor(0xFFFFFFFF);
183 Typeface font = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
184 mTextPaint.setTypeface(font);
185 mTextPaint.setTextAlign(Paint.Align.CENTER);
186
187 mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
188 mWarningTextPaint.setColor(mColors[1]);
189 font = Typeface.create("sans-serif", Typeface.BOLD);
190 mWarningTextPaint.setTypeface(font);
191 mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
192
193 mLightning = getResources().getDrawable(R.drawable.lightning);
194 }
195
196 @Override
197 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
198 mHeight = h;
199 mWidth = w;
200 mWarningTextPaint.setTextSize(h * 0.75f);
201 mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
202 }
203
204 private int getColorForLevel(int percent) {
205 int thresh, color = 0;
206 for (int i=0; i<mColors.length; i+=2) {
207 thresh = mColors[i];
208 color = mColors[i+1];
209 if (percent <= thresh) return color;
210 }
211 return color;
212 }
213
214 @Override
215 public void draw(Canvas c) {
216 final int level = mTracker.level;
217 float drawFrac = (float) level / 100f;
218 final int pt = getPaddingTop();
219 final int pl = getPaddingLeft();
220 final int pr = getPaddingRight();
221 final int pb = getPaddingBottom();
222 int height = mHeight - pt - pb;
223 int width = mWidth - pl - pr;
224
225 mButtonHeight = (int) (height * 0.12f);
226
227 final RectF frame = new RectF(0, 0, width, height);
228 frame.offset(pl, pt);
229
230 // Log.v("BatteryGauge", String.format("canvas: %dx%d frame: %s",
231 // c.getWidth(), c.getHeight(), frame.toString()));
232
233 final RectF buttonframe = new RectF(
234 frame.left + width * 0.25f,
235 frame.top,
236 frame.right - width * 0.25f,
237 frame.top + mButtonHeight);
238
239 frame.top += mButtonHeight;
240
241 // first, draw the battery shape
242 c.drawRect(frame, mFramePaint);
243
244 // fill 'er up
245 final int pct = mTracker.level;
246 final int color = getColorForLevel(pct);
247 mBatteryPaint.setColor(color);
248
249 if (level >= FULL) {
250 drawFrac = 1f;
251 } else if (level <= EMPTY) {
252 drawFrac = 0f;
253 }
254
255 c.drawRect(buttonframe,
256 drawFrac == 1f ? mBatteryPaint : mFramePaint);
257
258 RectF clip = new RectF(frame);
259 clip.top += (frame.height() * (1f - drawFrac));
260
261 c.save(Canvas.CLIP_SAVE_FLAG);
262 c.clipRect(clip);
263 c.drawRect(frame, mBatteryPaint);
264 c.restore();
265
266 if (level <= EMPTY) {
267 final float x = mWidth * 0.5f;
268 final float y = (mHeight + mWarningTextHeight) * 0.48f;
269 c.drawText(mWarningString, x, y, mWarningTextPaint);
270 } else if (mTracker.plugged) {
271 final Rect r = new Rect(
272 (int)frame.left + width / 4, (int)frame.top + height / 5,
273 (int)frame.right - width / 4, (int)frame.bottom - height / 6);
274 mLightning.setBounds(r);
275 mLightning.draw(c);
276 } else if (mShowPercent && !(mTracker.level == 100 && !SHOW_100_PERCENT)) {
277 mTextPaint.setTextSize(height *
278 (SINGLE_DIGIT_PERCENT ? 0.75f
279 : (mTracker.level == 100 ? 0.38f : 0.5f)));
280 mTextHeight = -mTextPaint.getFontMetrics().ascent;
281
282 final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (pct/10) : pct);
283 final float x = mWidth * 0.5f;
284 final float y = (mHeight + mTextHeight) * 0.47f;
285 c.drawText(str,
286 x,
287 y,
288 mTextPaint);
289
290// Paint pt = new Paint();
291// pt.setStrokeWidth(1f);
292// pt.setStyle(Paint.Style.STROKE);
293// pt.setColor(0xFFFF0000);
294// c.drawRect(x, y-mTextHeight, x+tw, y, pt);
295//
296// Slog.v(TAG, "tw=" + tw + " th=" + mTextHeight);
297//
298// pt.setColor(0xFFFF00FF);
299// c.drawRect(1, 1, mWidth, mHeight, pt);
300 }
301 }
302}