blob: 95b58e5e527fbb2b1ad22ec38585133d8dd2f112 [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
Jorim Jaggi5443cc52015-03-20 14:39:24 -070019import android.animation.ArgbEvaluator;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040020import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.res.Resources;
25import android.content.res.TypedArray;
John Spurlockf40d08f2015-05-29 10:48:22 -040026import android.database.ContentObserver;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040027import android.graphics.Canvas;
Jorim Jaggidd0107a2015-01-21 22:23:48 +010028import android.graphics.Color;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040029import android.graphics.Paint;
John Spurlockfceb7ed2013-09-07 12:39:49 -040030import android.graphics.Path;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040031import android.graphics.RectF;
32import android.graphics.Typeface;
John Spurlockf40d08f2015-05-29 10:48:22 -040033import android.net.Uri;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040034import android.os.BatteryManager;
John Spurlock3c875662013-08-31 15:07:25 -040035import android.os.Bundle;
John Spurlockf40d08f2015-05-29 10:48:22 -040036import android.os.Handler;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040037import android.provider.Settings;
38import android.util.AttributeSet;
39import android.view.View;
40
Jorim Jaggi708f7722014-08-20 17:30:38 +020041import com.android.systemui.statusbar.policy.BatteryController;
42
43public class BatteryMeterView extends View implements DemoMode,
44 BatteryController.BatteryStateChangeCallback {
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040045 public static final String TAG = BatteryMeterView.class.getSimpleName();
46 public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
John Spurlockf40d08f2015-05-29 10:48:22 -040047 public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040048
John Spurlock4b786ff2013-12-17 15:19:16 -050049 private static final boolean SINGLE_DIGIT_PERCENT = false;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040050
John Spurlock4b786ff2013-12-17 15:19:16 -050051 private static final int FULL = 96;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040052
John Spurlock4b786ff2013-12-17 15:19:16 -050053 private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction
John Spurlockcfab9902013-09-25 18:07:52 -040054
John Spurlock4b786ff2013-12-17 15:19:16 -050055 private final int[] mColors;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040056
John Spurlockf40d08f2015-05-29 10:48:22 -040057 private boolean mShowPercent;
Jorim Jaggi5172dc22014-09-02 14:59:06 +020058 private float mButtonHeightFraction;
Jorim Jaggia0221f32014-09-03 16:29:39 +020059 private float mSubpixelSmoothingLeft;
60 private float mSubpixelSmoothingRight;
John Spurlock4b786ff2013-12-17 15:19:16 -050061 private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040062 private float mTextHeight, mWarningTextHeight;
Jorim Jaggidd0107a2015-01-21 22:23:48 +010063 private int mIconTint = Color.WHITE;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040064
65 private int mHeight;
66 private int mWidth;
67 private String mWarningString;
John Spurlock3332ba52014-03-10 17:44:07 -040068 private final int mCriticalLevel;
Jorim Jaggidd0107a2015-01-21 22:23:48 +010069 private int mChargeColor;
John Spurlockfceb7ed2013-09-07 12:39:49 -040070 private final float[] mBoltPoints;
71 private final Path mBoltPath = new Path();
72
73 private final RectF mFrame = new RectF();
74 private final RectF mButtonFrame = new RectF();
John Spurlockd8595fd2013-11-05 11:55:04 -050075 private final RectF mBoltFrame = new RectF();
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040076
John Spurlock4b786ff2013-12-17 15:19:16 -050077 private final Path mShapePath = new Path();
78 private final Path mClipPath = new Path();
John Spurlock1fd0b932014-02-05 11:56:17 -050079 private final Path mTextPath = new Path();
John Spurlock4b786ff2013-12-17 15:19:16 -050080
Jorim Jaggi708f7722014-08-20 17:30:38 +020081 private BatteryController mBatteryController;
82 private boolean mPowerSaveEnabled;
83
Jorim Jaggi5443cc52015-03-20 14:39:24 -070084 private int mDarkModeBackgroundColor;
85 private int mDarkModeFillColor;
86
87 private int mLightModeBackgroundColor;
88 private int mLightModeFillColor;
89
John Spurlockf40d08f2015-05-29 10:48:22 -040090 private BatteryTracker mTracker = new BatteryTracker();
91 private final SettingObserver mSettingObserver = new SettingObserver();
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -040092
93 public BatteryMeterView(Context context) {
94 this(context, null, 0);
95 }
96
97 public BatteryMeterView(Context context, AttributeSet attrs) {
98 this(context, attrs, 0);
99 }
100
101 public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
102 super(context, attrs, defStyle);
103
104 final Resources res = context.getResources();
John Spurlock29786fc2014-02-04 17:55:47 -0500105 TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
106 defStyle, 0);
107 final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
Alan Viverette4a357cd2015-03-18 18:37:18 -0700108 context.getColor(R.color.batterymeter_frame_color));
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400109 TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
110 TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
111
112 final int N = levels.length();
113 mColors = new int[2*N];
114 for (int i=0; i<N; i++) {
115 mColors[2*i] = levels.getInt(i, 0);
116 mColors[2*i+1] = colors.getColor(i, 0);
117 }
John Spurlockfceb7ed2013-09-07 12:39:49 -0400118 levels.recycle();
119 colors.recycle();
John Spurlock29786fc2014-02-04 17:55:47 -0500120 atts.recycle();
John Spurlockf40d08f2015-05-29 10:48:22 -0400121 updateShowPercent();
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400122 mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
John Spurlock3332ba52014-03-10 17:44:07 -0400123 mCriticalLevel = mContext.getResources().getInteger(
124 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
Jorim Jaggi5172dc22014-09-02 14:59:06 +0200125 mButtonHeightFraction = context.getResources().getFraction(
126 R.fraction.battery_button_height_fraction, 1, 1);
Jorim Jaggia0221f32014-09-03 16:29:39 +0200127 mSubpixelSmoothingLeft = context.getResources().getFraction(
128 R.fraction.battery_subpixel_smoothing_left, 1, 1);
129 mSubpixelSmoothingRight = context.getResources().getFraction(
130 R.fraction.battery_subpixel_smoothing_right, 1, 1);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400131
Chris Craik6a49dde2015-05-12 10:28:14 -0700132 mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
John Spurlock29786fc2014-02-04 17:55:47 -0500133 mFramePaint.setColor(frameColor);
John Spurlockcfab9902013-09-25 18:07:52 -0400134 mFramePaint.setDither(true);
135 mFramePaint.setStrokeWidth(0);
136 mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
John Spurlockcfab9902013-09-25 18:07:52 -0400137
Chris Craik6a49dde2015-05-12 10:28:14 -0700138 mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
John Spurlockcfab9902013-09-25 18:07:52 -0400139 mBatteryPaint.setDither(true);
140 mBatteryPaint.setStrokeWidth(0);
141 mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400142
Chris Craik6a49dde2015-05-12 10:28:14 -0700143 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
John Spurlock4b786ff2013-12-17 15:19:16 -0500144 Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400145 mTextPaint.setTypeface(font);
146 mTextPaint.setTextAlign(Paint.Align.CENTER);
147
Chris Craik6a49dde2015-05-12 10:28:14 -0700148 mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400149 mWarningTextPaint.setColor(mColors[1]);
150 font = Typeface.create("sans-serif", Typeface.BOLD);
151 mWarningTextPaint.setTypeface(font);
152 mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
153
Alan Viverette4a357cd2015-03-18 18:37:18 -0700154 mChargeColor = context.getColor(R.color.batterymeter_charge_color);
John Spurlockfceb7ed2013-09-07 12:39:49 -0400155
Chris Craik6a49dde2015-05-12 10:28:14 -0700156 mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700157 mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
John Spurlockfceb7ed2013-09-07 12:39:49 -0400158 mBoltPoints = loadBoltPoints(res);
Jorim Jaggi5443cc52015-03-20 14:39:24 -0700159
160 mDarkModeBackgroundColor =
161 context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
162 mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
163 mLightModeBackgroundColor =
164 context.getColor(R.color.light_mode_icon_color_dual_tone_background);
165 mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
John Spurlockfceb7ed2013-09-07 12:39:49 -0400166 }
167
John Spurlockf40d08f2015-05-29 10:48:22 -0400168 @Override
169 public void onAttachedToWindow() {
170 super.onAttachedToWindow();
171
172 IntentFilter filter = new IntentFilter();
173 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
174 filter.addAction(ACTION_LEVEL_TEST);
175 final Intent sticky = getContext().registerReceiver(mTracker, filter);
176 if (sticky != null) {
177 // preload the battery level
178 mTracker.onReceive(getContext(), sticky);
179 }
180 mBatteryController.addStateChangedCallback(this);
181 getContext().getContentResolver().registerContentObserver(
182 Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
183 }
184
185 @Override
186 public void onDetachedFromWindow() {
187 super.onDetachedFromWindow();
188
189 getContext().unregisterReceiver(mTracker);
190 mBatteryController.removeStateChangedCallback(this);
191 getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
192 }
193
Jorim Jaggi708f7722014-08-20 17:30:38 +0200194 public void setBatteryController(BatteryController batteryController) {
195 mBatteryController = batteryController;
196 mPowerSaveEnabled = mBatteryController.isPowerSave();
197 }
198
199 @Override
200 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
201 // TODO: Use this callback instead of own broadcast receiver.
202 }
203
204 @Override
205 public void onPowerSaveChanged() {
206 mPowerSaveEnabled = mBatteryController.isPowerSave();
207 invalidate();
208 }
209
John Spurlockfceb7ed2013-09-07 12:39:49 -0400210 private static float[] loadBoltPoints(Resources res) {
211 final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
212 int maxX = 0, maxY = 0;
213 for (int i = 0; i < pts.length; i += 2) {
214 maxX = Math.max(maxX, pts[i]);
215 maxY = Math.max(maxY, pts[i + 1]);
216 }
217 final float[] ptsF = new float[pts.length];
218 for (int i = 0; i < pts.length; i += 2) {
219 ptsF[i] = (float)pts[i] / maxX;
220 ptsF[i + 1] = (float)pts[i + 1] / maxY;
221 }
222 return ptsF;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400223 }
224
225 @Override
226 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
227 mHeight = h;
228 mWidth = w;
229 mWarningTextPaint.setTextSize(h * 0.75f);
230 mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
231 }
232
John Spurlockf40d08f2015-05-29 10:48:22 -0400233 private void updateShowPercent() {
234 mShowPercent = 0 != Settings.System.getInt(getContext().getContentResolver(),
235 SHOW_PERCENT_SETTING, 0);
236 }
237
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400238 private int getColorForLevel(int percent) {
Jorim Jaggi708f7722014-08-20 17:30:38 +0200239
240 // If we are in power save mode, always use the normal color.
241 if (mPowerSaveEnabled) {
242 return mColors[mColors.length-1];
243 }
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400244 int thresh, color = 0;
245 for (int i=0; i<mColors.length; i+=2) {
246 thresh = mColors[i];
247 color = mColors[i+1];
Jorim Jaggidd0107a2015-01-21 22:23:48 +0100248 if (percent <= thresh) {
249
250 // Respect tinting for "normal" level
251 if (i == mColors.length-2) {
252 return mIconTint;
253 } else {
254 return color;
255 }
256 }
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400257 }
258 return color;
259 }
260
Jorim Jaggi5443cc52015-03-20 14:39:24 -0700261 public void setDarkIntensity(float darkIntensity) {
262 int backgroundColor = getBackgroundColor(darkIntensity);
263 int fillColor = getFillColor(darkIntensity);
264 mIconTint = fillColor;
265 mFramePaint.setColor(backgroundColor);
Xiyuan Xiab07ce172015-04-24 13:53:33 -0700266 mBoltPaint.setColor(fillColor);
Jorim Jaggi5443cc52015-03-20 14:39:24 -0700267 mChargeColor = fillColor;
Jorim Jaggidd0107a2015-01-21 22:23:48 +0100268 invalidate();
269 }
270
Jorim Jaggi5443cc52015-03-20 14:39:24 -0700271 private int getBackgroundColor(float darkIntensity) {
272 return getColorForDarkIntensity(
273 darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor);
274 }
275
276 private int getFillColor(float darkIntensity) {
277 return getColorForDarkIntensity(
278 darkIntensity, mLightModeFillColor, mDarkModeFillColor);
279 }
280
281 private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
282 return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
283 }
284
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400285 @Override
286 public void draw(Canvas c) {
John Spurlock3c875662013-08-31 15:07:25 -0400287 BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
288 final int level = tracker.level;
Daniel Sandler05021e52013-10-14 10:27:45 -0400289
290 if (level == BatteryTracker.UNKNOWN_LEVEL) return;
291
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400292 float drawFrac = (float) level / 100f;
293 final int pt = getPaddingTop();
294 final int pl = getPaddingLeft();
295 final int pr = getPaddingRight();
296 final int pb = getPaddingBottom();
John Spurlock4b786ff2013-12-17 15:19:16 -0500297 final int height = mHeight - pt - pb;
298 final int width = mWidth - pl - pr;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400299
Jorim Jaggi5172dc22014-09-02 14:59:06 +0200300 final int buttonHeight = (int) (height * mButtonHeightFraction);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400301
John Spurlockfceb7ed2013-09-07 12:39:49 -0400302 mFrame.set(0, 0, width, height);
303 mFrame.offset(pl, pt);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400304
John Spurlock4b786ff2013-12-17 15:19:16 -0500305 // button-frame: area above the battery body
John Spurlockfceb7ed2013-09-07 12:39:49 -0400306 mButtonFrame.set(
Jorim Jaggia0221f32014-09-03 16:29:39 +0200307 mFrame.left + Math.round(width * 0.25f),
John Spurlockfceb7ed2013-09-07 12:39:49 -0400308 mFrame.top,
Jorim Jaggia0221f32014-09-03 16:29:39 +0200309 mFrame.right - Math.round(width * 0.25f),
John Spurlock4b786ff2013-12-17 15:19:16 -0500310 mFrame.top + buttonHeight);
John Spurlockcfab9902013-09-25 18:07:52 -0400311
Jorim Jaggia0221f32014-09-03 16:29:39 +0200312 mButtonFrame.top += mSubpixelSmoothingLeft;
313 mButtonFrame.left += mSubpixelSmoothingLeft;
314 mButtonFrame.right -= mSubpixelSmoothingRight;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400315
John Spurlock4b786ff2013-12-17 15:19:16 -0500316 // frame: battery body area
317 mFrame.top += buttonHeight;
Jorim Jaggia0221f32014-09-03 16:29:39 +0200318 mFrame.left += mSubpixelSmoothingLeft;
319 mFrame.top += mSubpixelSmoothingLeft;
320 mFrame.right -= mSubpixelSmoothingRight;
321 mFrame.bottom -= mSubpixelSmoothingRight;
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400322
John Spurlock4b786ff2013-12-17 15:19:16 -0500323 // set the battery charging color
324 mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level));
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400325
326 if (level >= FULL) {
327 drawFrac = 1f;
John Spurlock3332ba52014-03-10 17:44:07 -0400328 } else if (level <= mCriticalLevel) {
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400329 drawFrac = 0f;
330 }
331
John Spurlock4b786ff2013-12-17 15:19:16 -0500332 final float levelTop = drawFrac == 1f ? mButtonFrame.top
333 : (mFrame.top + (mFrame.height() * (1f - drawFrac)));
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400334
John Spurlock4b786ff2013-12-17 15:19:16 -0500335 // define the battery shape
336 mShapePath.reset();
337 mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top);
338 mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
339 mShapePath.lineTo(mButtonFrame.right, mFrame.top);
340 mShapePath.lineTo(mFrame.right, mFrame.top);
341 mShapePath.lineTo(mFrame.right, mFrame.bottom);
342 mShapePath.lineTo(mFrame.left, mFrame.bottom);
343 mShapePath.lineTo(mFrame.left, mFrame.top);
344 mShapePath.lineTo(mButtonFrame.left, mFrame.top);
345 mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400346
John Spurlockd19877f2013-09-17 11:03:03 -0400347 if (tracker.plugged) {
John Spurlock4b786ff2013-12-17 15:19:16 -0500348 // define the bolt shape
John Spurlockd8595fd2013-11-05 11:55:04 -0500349 final float bl = mFrame.left + mFrame.width() / 4.5f;
350 final float bt = mFrame.top + mFrame.height() / 6f;
351 final float br = mFrame.right - mFrame.width() / 7f;
352 final float bb = mFrame.bottom - mFrame.height() / 10f;
John Spurlockfceb7ed2013-09-07 12:39:49 -0400353 if (mBoltFrame.left != bl || mBoltFrame.top != bt
354 || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
355 mBoltFrame.set(bl, bt, br, bb);
356 mBoltPath.reset();
357 mBoltPath.moveTo(
358 mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
359 mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
360 for (int i = 2; i < mBoltPoints.length; i += 2) {
361 mBoltPath.lineTo(
362 mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
363 mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
364 }
365 mBoltPath.lineTo(
366 mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
367 mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
368 }
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400369
John Spurlock4b786ff2013-12-17 15:19:16 -0500370 float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
371 boltPct = Math.min(Math.max(boltPct, 0), 1);
372 if (boltPct <= BOLT_LEVEL_THRESHOLD) {
373 // draw the bolt if opaque
374 c.drawPath(mBoltPath, mBoltPaint);
375 } else {
376 // otherwise cut the bolt out of the overall shape
377 mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
378 }
379 }
380
John Spurlock1fd0b932014-02-05 11:56:17 -0500381 // compute percentage text
382 boolean pctOpaque = false;
383 float pctX = 0, pctY = 0;
384 String pctText = null;
John Spurlockf40d08f2015-05-29 10:48:22 -0400385 if (!tracker.plugged && level > mCriticalLevel && mShowPercent) {
John Spurlock1fd0b932014-02-05 11:56:17 -0500386 mTextPaint.setColor(getColorForLevel(level));
387 mTextPaint.setTextSize(height *
388 (SINGLE_DIGIT_PERCENT ? 0.75f
389 : (tracker.level == 100 ? 0.38f : 0.5f)));
390 mTextHeight = -mTextPaint.getFontMetrics().ascent;
391 pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
392 pctX = mWidth * 0.5f;
393 pctY = (mHeight + mTextHeight) * 0.47f;
394 pctOpaque = levelTop > pctY;
395 if (!pctOpaque) {
396 mTextPath.reset();
397 mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
398 // cut the percentage text out of the overall shape
399 mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
400 }
401 }
402
John Spurlock4b786ff2013-12-17 15:19:16 -0500403 // draw the battery shape background
404 c.drawPath(mShapePath, mFramePaint);
405
406 // draw the battery shape, clipped to charging level
407 mFrame.top = levelTop;
408 mClipPath.reset();
409 mClipPath.addRect(mFrame, Path.Direction.CCW);
410 mShapePath.op(mClipPath, Path.Op.INTERSECT);
411 c.drawPath(mShapePath, mBatteryPaint);
412
413 if (!tracker.plugged) {
John Spurlock3332ba52014-03-10 17:44:07 -0400414 if (level <= mCriticalLevel) {
John Spurlock4b786ff2013-12-17 15:19:16 -0500415 // draw the warning text
416 final float x = mWidth * 0.5f;
417 final float y = (mHeight + mWarningTextHeight) * 0.48f;
418 c.drawText(mWarningString, x, y, mWarningTextPaint);
John Spurlock1fd0b932014-02-05 11:56:17 -0500419 } else if (pctOpaque) {
John Spurlock4b786ff2013-12-17 15:19:16 -0500420 // draw the percentage text
John Spurlock1fd0b932014-02-05 11:56:17 -0500421 c.drawText(pctText, pctX, pctY, mTextPaint);
John Spurlock4b786ff2013-12-17 15:19:16 -0500422 }
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400423 }
424 }
John Spurlock3c875662013-08-31 15:07:25 -0400425
Jorim Jaggi0d266892014-07-28 14:49:13 +0200426 @Override
427 public boolean hasOverlappingRendering() {
428 return false;
429 }
430
John Spurlock3c875662013-08-31 15:07:25 -0400431 private boolean mDemoMode;
432 private BatteryTracker mDemoTracker = new BatteryTracker();
433
434 @Override
435 public void dispatchDemoCommand(String command, Bundle args) {
436 if (!mDemoMode && command.equals(COMMAND_ENTER)) {
437 mDemoMode = true;
438 mDemoTracker.level = mTracker.level;
439 mDemoTracker.plugged = mTracker.plugged;
440 } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
441 mDemoMode = false;
442 postInvalidate();
443 } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
444 String level = args.getString("level");
445 String plugged = args.getString("plugged");
446 if (level != null) {
447 mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
448 }
449 if (plugged != null) {
450 mDemoTracker.plugged = Boolean.parseBoolean(plugged);
451 }
452 postInvalidate();
453 }
454 }
John Spurlockf40d08f2015-05-29 10:48:22 -0400455
456 private final class BatteryTracker extends BroadcastReceiver {
457 public static final int UNKNOWN_LEVEL = -1;
458
459 // current battery status
460 int level = UNKNOWN_LEVEL;
461 String percentStr;
462 int plugType;
463 boolean plugged;
464 int health;
465 int status;
466 String technology;
467 int voltage;
468 int temperature;
469 boolean testmode = false;
470
471 @Override
472 public void onReceive(Context context, Intent intent) {
473 final String action = intent.getAction();
474 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
475 if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
476
477 level = (int)(100f
478 * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
479 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
480
481 plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
482 plugged = plugType != 0;
483 health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
484 BatteryManager.BATTERY_HEALTH_UNKNOWN);
485 status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
486 BatteryManager.BATTERY_STATUS_UNKNOWN);
487 technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
488 voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
489 temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
490
491 setContentDescription(
492 context.getString(R.string.accessibility_battery_level, level));
493 postInvalidate();
494 } else if (action.equals(ACTION_LEVEL_TEST)) {
495 testmode = true;
496 post(new Runnable() {
497 int curLevel = 0;
498 int incr = 1;
499 int saveLevel = level;
500 int savePlugged = plugType;
501 Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
502 @Override
503 public void run() {
504 if (curLevel < 0) {
505 testmode = false;
506 dummy.putExtra("level", saveLevel);
507 dummy.putExtra("plugged", savePlugged);
508 dummy.putExtra("testmode", false);
509 } else {
510 dummy.putExtra("level", curLevel);
511 dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
512 : 0);
513 dummy.putExtra("testmode", true);
514 }
515 getContext().sendBroadcast(dummy);
516
517 if (!testmode) return;
518
519 curLevel += incr;
520 if (curLevel == 100) {
521 incr *= -1;
522 }
523 postDelayed(this, 200);
524 }
525 });
526 }
527 }
528 }
529
530 private final class SettingObserver extends ContentObserver {
531 public SettingObserver() {
532 super(new Handler());
533 }
534
535 @Override
536 public void onChange(boolean selfChange, Uri uri) {
537 super.onChange(selfChange, uri);
538 updateShowPercent();
539 postInvalidate();
540 }
541 }
542
Daniel Sandlerdfaf3bd2013-04-12 01:39:02 -0400543}