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