summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/BatteryMeterView.java')
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/BatteryMeterView.java805
1 files changed, 313 insertions, 492 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index e606156..06c2957 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013-14 The Android Open Source Project
+ * Copyright (C) 2016 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,43 +17,47 @@
package com.android.systemui;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryStateRegistar;
-
import android.animation.ArgbEvaluator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.content.res.ThemeConfig;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Typeface;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.os.BatteryManager;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryStateRegistar;
+
+import org.cyanogenmod.graphics.drawable.StopMotionVectorDrawable;
+
public class BatteryMeterView extends View implements DemoMode,
BatteryController.BatteryStateChangeCallback {
public static final String TAG = BatteryMeterView.class.getSimpleName();
public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
- private static final int FULL = 96;
-
- private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction
-
private final int[] mColors;
protected boolean mShowPercent = true;
- private float mButtonHeightFraction;
- private float mSubpixelSmoothingLeft;
- private float mSubpixelSmoothingRight;
public enum BatteryMeterMode {
BATTERY_METER_GONE,
@@ -66,14 +71,9 @@ public class BatteryMeterView extends View implements DemoMode,
private int mWidth;
private String mWarningString;
private final int mCriticalLevel;
- private final int mFrameColor;
private boolean mAnimationsEnabled;
- private final Path mShapePath = new Path();
- private final Path mClipPath = new Path();
- private final Path mTextPath = new Path();
-
private BatteryStateRegistar mBatteryStateRegistar;
private BatteryController mBatteryController;
private boolean mPowerSaveEnabled;
@@ -94,6 +94,9 @@ public class BatteryMeterView extends View implements DemoMode,
private BatteryMeterDrawable mBatteryMeterDrawable;
private int mIconTint = Color.WHITE;
+ private int mCurrentBackgroundColor = 0;
+ private int mCurrentFillColor = 0;
+
protected class BatteryTracker extends BroadcastReceiver {
public static final int UNKNOWN_LEVEL = -1;
@@ -225,8 +228,6 @@ public class BatteryMeterView extends View implements DemoMode,
final Resources res = context.getResources();
TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
defStyle, 0);
- mFrameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
- res.getColor(R.color.batterymeter_frame_color));
TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
@@ -242,12 +243,6 @@ public class BatteryMeterView extends View implements DemoMode,
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
mCriticalLevel = getContext().getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
- mButtonHeightFraction = context.getResources().getFraction(
- R.fraction.battery_button_height_fraction, 1, 1);
- mSubpixelSmoothingLeft = context.getResources().getFraction(
- R.fraction.battery_subpixel_smoothing_left, 1, 1);
- mSubpixelSmoothingRight = context.getResources().getFraction(
- R.fraction.battery_subpixel_smoothing_right, 1, 1);
mDarkModeBackgroundColor =
context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
@@ -260,17 +255,13 @@ public class BatteryMeterView extends View implements DemoMode,
}
protected BatteryMeterDrawable createBatteryMeterDrawable(BatteryMeterMode mode) {
- Resources res = mContext.getResources();
+ Resources res = getResources();
switch (mode) {
- case BATTERY_METER_CIRCLE:
- return new CircleBatteryMeterDrawable(res);
- case BATTERY_METER_ICON_LANDSCAPE:
- return new NormalBatteryMeterDrawable(res, true);
case BATTERY_METER_TEXT:
case BATTERY_METER_GONE:
return null;
default:
- return new NormalBatteryMeterDrawable(res, false);
+ return new AllInOneBatteryMeterDrawable(res, mode);
}
}
@@ -279,13 +270,8 @@ public class BatteryMeterView extends View implements DemoMode,
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
- if (mMeterMode == BatteryMeterMode.BATTERY_METER_CIRCLE) {
- height += (CircleBatteryMeterDrawable.STROKE_WITH / 3);
- width = height;
- } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) {
+ if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) {
onSizeChanged(width, height, 0, 0); // Force a size changed event
- } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) {
- width = (int)(height * 1.2f);
}
setMeasuredDimension(width, height);
@@ -378,11 +364,6 @@ public class BatteryMeterView extends View implements DemoMode,
mBatteryMeterDrawable.onDispose();
}
mBatteryMeterDrawable = createBatteryMeterDrawable(mode);
- if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_PORTRAIT ||
- mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) {
- ((NormalBatteryMeterDrawable)mBatteryMeterDrawable).loadBoltPoints(
- mContext.getResources());
- }
if (tracker.present) {
setVisibility(View.VISIBLE);
requestLayout();
@@ -394,7 +375,6 @@ public class BatteryMeterView extends View implements DemoMode,
}
public int getColorForLevel(int percent) {
-
// If we are in power save mode, always use the normal color.
if (mPowerSaveEnabled) {
return mColors[mColors.length-1];
@@ -418,9 +398,9 @@ public class BatteryMeterView extends View implements DemoMode,
public void setDarkIntensity(float darkIntensity) {
if (mBatteryMeterDrawable != null) {
- int backgroundColor = getBackgroundColor(darkIntensity);
- int fillColor = getFillColor(darkIntensity);
- mBatteryMeterDrawable.setDarkIntensity(backgroundColor, fillColor);
+ mCurrentBackgroundColor = getBackgroundColor(darkIntensity);
+ mCurrentFillColor = getFillColor(darkIntensity);
+ mBatteryMeterDrawable.setDarkIntensity(mCurrentBackgroundColor, mCurrentFillColor);
}
}
@@ -482,253 +462,83 @@ public class BatteryMeterView extends View implements DemoMode,
void setDarkIntensity(int backgroundColor, int fillColor);
}
- protected class NormalBatteryMeterDrawable implements BatteryMeterDrawable {
+ protected class AllInOneBatteryMeterDrawable implements BatteryMeterDrawable {
private static final boolean SINGLE_DIGIT_PERCENT = false;
private static final boolean SHOW_100_PERCENT = false;
private boolean mDisposed;
- protected final boolean mHorizontal;
+ private boolean mIsAnimating; // stores charge-animation status to remove callbacks
+
+ private float mTextX, mTextY; // precalculated position for drawText() to appear centered
- private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
- private float mTextHeight, mWarningTextHeight;
+ private boolean mInitialized;
- private int mChargeColor;
- private final float[] mBoltPoints;
- private final Path mBoltPath = new Path();
+ private Paint mTextAndBoltPaint;
+ private Paint mWarningTextPaint;
+ private Paint mClearPaint;
- private final RectF mFrame = new RectF();
- private final RectF mButtonFrame = new RectF();
- private final RectF mBoltFrame = new RectF();
+ private LayerDrawable mBatteryDrawable;
+ private Drawable mFrameDrawable;
+ private StopMotionVectorDrawable mLevelDrawable;
+ private Drawable mBoltDrawable;
- public NormalBatteryMeterDrawable(Resources res, boolean horizontal) {
+ private BatteryMeterMode mMode;
+ private int mTextGravity;
+
+ public AllInOneBatteryMeterDrawable(Resources res, BatteryMeterMode mode) {
super();
- mHorizontal = horizontal;
- mDisposed = false;
- mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mFramePaint.setColor(mFrameColor);
- mFramePaint.setDither(true);
- mFramePaint.setStrokeWidth(0);
- mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ loadBatteryDrawables(res, mode);
- mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBatteryPaint.setDither(true);
- mBatteryPaint.setStrokeWidth(0);
- mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ mMode = mode;
+ mDisposed = false;
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ // load text gravity and blend mode
+ int[] attrs = new int[] {android.R.attr.gravity, R.attr.blendMode};
+ int resId = getBatteryDrawableStyleResourceForMode(mode);
+ PorterDuff.Mode xferMode = PorterDuff.Mode.XOR;
+ if (resId != 0) {
+ TypedArray a = getContext().obtainStyledAttributes(
+ getBatteryDrawableStyleResourceForMode(mode), attrs);
+ mTextGravity = a.getInt(0, Gravity.CENTER);
+ xferMode = PorterDuff.intToMode(a.getInt(1,
+ PorterDuff.modeToInt(PorterDuff.Mode.XOR)));
+ } else {
+ mTextGravity = Gravity.CENTER;
+ }
+ Log.d(TAG, "mTextGravity=" + mTextGravity);
+
+ mTextAndBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
- mTextPaint.setTypeface(font);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
+ mTextAndBoltPaint.setTypeface(font);
+ mTextAndBoltPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity));
+ mTextAndBoltPaint.setXfermode(new PorterDuffXfermode(xferMode));
+ mTextAndBoltPaint.setColor(mCurrentFillColor != 0
+ ? mCurrentFillColor
+ : res.getColor(R.color.batterymeter_bolt_color));
mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mWarningTextPaint.setColor(mColors[1]);
font = Typeface.create("sans-serif", Typeface.BOLD);
mWarningTextPaint.setTypeface(font);
- mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
-
- mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);
+ mWarningTextPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity));
- mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));
- mBoltPoints = loadBoltPoints(res);
+ mClearPaint = new Paint();
+ mClearPaint.setColor(0);
}
@Override
public void onDraw(Canvas c, BatteryTracker tracker) {
if (mDisposed) return;
- final int level = tracker.level;
-
- if (level == BatteryTracker.UNKNOWN_LEVEL) return;
-
- float drawFrac = (float) level / 100f;
- final int pt = getPaddingTop() + (mHorizontal ? (int)(mHeight * 0.12f) : 0);
- final int pl = getPaddingLeft();
- final int pr = getPaddingRight();
- final int pb = getPaddingBottom() + (mHorizontal ? (int)(mHeight * 0.08f) : 0);
- final int height = mHeight - pt - pb;
- final int width = mWidth - pl - pr;
-
- final int buttonHeight = (int) ((mHorizontal ? width : height) * mButtonHeightFraction);
-
- mFrame.set(0, 0, width, height);
- mFrame.offset(pl, pt);
-
- if (mHorizontal) {
- mButtonFrame.set(
- /*cover frame border of intersecting area*/
- width - buttonHeight - mFrame.left,
- mFrame.top + Math.round(height * 0.25f),
- mFrame.right,
- mFrame.bottom - Math.round(height * 0.25f));
-
- mButtonFrame.top += mSubpixelSmoothingLeft;
- mButtonFrame.bottom -= mSubpixelSmoothingRight;
- mButtonFrame.right -= mSubpixelSmoothingRight;
- } else {
- // button-frame: area above the battery body
- mButtonFrame.set(
- mFrame.left + Math.round(width * 0.25f),
- mFrame.top,
- mFrame.right - Math.round(width * 0.25f),
- mFrame.top + buttonHeight);
-
- mButtonFrame.top += mSubpixelSmoothingLeft;
- mButtonFrame.left += mSubpixelSmoothingLeft;
- mButtonFrame.right -= mSubpixelSmoothingRight;
- }
-
- // frame: battery body area
-
- if (mHorizontal) {
- mFrame.right -= buttonHeight;
- } else {
- mFrame.top += buttonHeight;
- }
- mFrame.left += mSubpixelSmoothingLeft;
- mFrame.top += mSubpixelSmoothingLeft;
- mFrame.right -= mSubpixelSmoothingRight;
- mFrame.bottom -= mSubpixelSmoothingRight;
-
- // set the battery charging color
- mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level));
-
- if (level >= FULL) {
- drawFrac = 1f;
- } else if (level <= mCriticalLevel) {
- drawFrac = 0f;
- }
-
- final float levelTop;
-
- if (drawFrac == 1f) {
- if (mHorizontal) {
- levelTop = mButtonFrame.right;
- } else {
- levelTop = mButtonFrame.top;
- }
- } else {
- if (mHorizontal) {
- levelTop = (mFrame.right - (mFrame.width() * (1f - drawFrac)));
- } else {
- levelTop = (mFrame.top + (mFrame.height() * (1f - drawFrac)));
- }
- }
-
- // define the battery shape
- mShapePath.reset();
- mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top);
- if (mHorizontal) {
- mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
- mShapePath.lineTo(mButtonFrame.right, mButtonFrame.bottom);
- mShapePath.lineTo(mButtonFrame.left, mButtonFrame.bottom);
- mShapePath.lineTo(mFrame.right, mFrame.bottom);
- mShapePath.lineTo(mFrame.left, mFrame.bottom);
- mShapePath.lineTo(mFrame.left, mFrame.top);
- mShapePath.lineTo(mButtonFrame.left, mFrame.top);
- mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
- } else {
- mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
- mShapePath.lineTo(mButtonFrame.right, mFrame.top);
- mShapePath.lineTo(mFrame.right, mFrame.top);
- mShapePath.lineTo(mFrame.right, mFrame.bottom);
- mShapePath.lineTo(mFrame.left, mFrame.bottom);
- mShapePath.lineTo(mFrame.left, mFrame.top);
- mShapePath.lineTo(mButtonFrame.left, mFrame.top);
- mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
+ if (!mInitialized) {
+ init();
}
- if (tracker.plugged) {
- // define the bolt shape
- final float bl = mFrame.left + mFrame.width() / (mHorizontal ? 9f : 4.5f);
- final float bt = mFrame.top + mFrame.height() / (mHorizontal ? 4.5f : 6f);
- final float br = mFrame.right - mFrame.width() / (mHorizontal ? 6f : 7f);
- final float bb = mFrame.bottom - mFrame.height() / (mHorizontal ? 7f : 10f);
- if (mBoltFrame.left != bl || mBoltFrame.top != bt
- || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
- mBoltFrame.set(bl, bt, br, bb);
- mBoltPath.reset();
- mBoltPath.moveTo(
- mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
- for (int i = 2; i < mBoltPoints.length; i += 2) {
- mBoltPath.lineTo(
- mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
- }
- mBoltPath.lineTo(
- mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
- }
-
- float boltPct = mHorizontal ?
- (mBoltFrame.left - levelTop) / (mBoltFrame.left - mBoltFrame.right) :
- (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
- boltPct = Math.min(Math.max(boltPct, 0), 1);
- if (boltPct <= BOLT_LEVEL_THRESHOLD) {
- // draw the bolt if opaque
- c.drawPath(mBoltPath, mBoltPaint);
- } else {
- // otherwise cut the bolt out of the overall shape
- mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
- }
- }
-
- // compute percentage text
- boolean pctOpaque = false;
- float pctX = 0, pctY = 0;
- String pctText = null;
- if (!tracker.plugged && level > mCriticalLevel && mShowPercent) {
- mTextPaint.setColor(getColorForLevel(level));
- final float full = mHorizontal ? 0.60f : 0.45f;
- final float nofull = mHorizontal ? 0.75f : 0.6f;
- final float single = mHorizontal ? 0.86f : 0.75f;
- mTextPaint.setTextSize(height *
- (SINGLE_DIGIT_PERCENT ? single
- : (tracker.level == 100 ? full : nofull)));
- mTextHeight = -mTextPaint.getFontMetrics().ascent;
- pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
- pctX = mWidth * 0.5f;
- pctY = (mHeight + mTextHeight) * 0.47f;
- if (mHorizontal) {
- pctOpaque = pctX > levelTop;
- } else {
- pctOpaque = levelTop > pctY;
- }
- if (!pctOpaque) {
- mTextPath.reset();
- mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
- // cut the percentage text out of the overall shape
- mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
- }
- }
-
- // draw the battery shape background
- c.drawPath(mShapePath, mFramePaint);
-
- // draw the battery shape, clipped to charging level
- if (mHorizontal) {
- mFrame.right = levelTop;
- } else {
- mFrame.top = levelTop;
- }
- mClipPath.reset();
- mClipPath.addRect(mFrame, Path.Direction.CCW);
- mShapePath.op(mClipPath, Path.Op.INTERSECT);
- c.drawPath(mShapePath, mBatteryPaint);
-
- if (!tracker.plugged) {
- if (level <= mCriticalLevel) {
- // draw the warning text
- final float x = mWidth * 0.5f;
- final float y = (mHeight + mWarningTextHeight) * 0.48f;
- c.drawText(mWarningString, x, y, mWarningTextPaint);
- } else if (pctOpaque) {
- // draw the percentage text
- c.drawText(pctText, pctX, pctY, mTextPaint);
- }
+ drawBattery(c, tracker);
+ if (mAnimationsEnabled) {
+ // TODO: Allow custom animations to be used
}
}
@@ -740,282 +550,293 @@ public class BatteryMeterView extends View implements DemoMode,
@Override
public void setDarkIntensity(int backgroundColor, int fillColor) {
mIconTint = fillColor;
- mFramePaint.setColor(backgroundColor);
- mBoltPaint.setColor(fillColor);
- mChargeColor = fillColor;
+ // Make bolt fully opaque for increased visibility
+ mBoltDrawable.setTint(0xff000000 | fillColor);
+ mFrameDrawable.setTint(backgroundColor);
+ updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable);
invalidate();
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
- mHeight = h;
- mWidth = w;
- mWarningTextPaint.setTextSize(h * 0.75f);
- mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
- }
-
- private float[] loadBoltPoints(Resources res) {
- final int[] pts = res.getIntArray(getBoltPointsArrayResource());
- int maxX = 0, maxY = 0;
- for (int i = 0; i < pts.length; i += 2) {
- maxX = Math.max(maxX, pts[i]);
- maxY = Math.max(maxY, pts[i + 1]);
- }
- final float[] ptsF = new float[pts.length];
- for (int i = 0; i < pts.length; i += 2) {
- ptsF[i] = (float)pts[i] / maxX;
- ptsF[i + 1] = (float)pts[i + 1] / maxY;
- }
- return ptsF;
+ init();
}
- protected int getBoltPointsArrayResource() {
- return mHorizontal
- ? R.array.batterymeter_inverted_bolt_points
- : R.array.batterymeter_bolt_points;
+ private boolean isThemeApplied() {
+ ThemeConfig themeConfig = ThemeConfig.getBootTheme(getContext().getContentResolver());
+ return themeConfig != null &&
+ !ThemeConfig.SYSTEM_DEFAULT.equals(themeConfig.getOverlayForStatusBar());
}
- }
- protected class CircleBatteryMeterDrawable implements BatteryMeterDrawable {
- private static final boolean SINGLE_DIGIT_PERCENT = false;
- private static final boolean SHOW_100_PERCENT = false;
-
- private static final int FULL = 96;
+ private void checkBatteryMeterDrawableValid(Resources res, BatteryMeterMode mode) {
+ final int resId = getBatteryDrawableResourceForMode(mode);
+ final Drawable batteryDrawable;
+ try {
+ batteryDrawable = res.getDrawable(resId);
+ } catch (Resources.NotFoundException e) {
+ throw new BatteryMeterDrawableException(res.getResourceName(resId) + " is an " +
+ "invalid drawable", e);
+ }
- public static final float STROKE_WITH = 6.5f;
+ // check that the drawable is a LayerDrawable
+ if (!(batteryDrawable instanceof LayerDrawable)) {
+ throw new BatteryMeterDrawableException("Expected a LayerDrawable but received a " +
+ batteryDrawable.getClass().getSimpleName());
+ }
- private boolean mDisposed;
+ final LayerDrawable layerDrawable = (LayerDrawable) batteryDrawable;
+ final Drawable frame = layerDrawable.findDrawableByLayerId(R.id.battery_frame);
+ final Drawable level = layerDrawable.findDrawableByLayerId(R.id.battery_fill);
+ final Drawable bolt = layerDrawable.findDrawableByLayerId(
+ R.id.battery_charge_indicator);
+ // now check that the required layers exist and are of the correct type
+ if (frame == null) {
+ throw new BatteryMeterDrawableException("Missing battery_frame drawble");
+ }
+ if (bolt == null) {
+ throw new BatteryMeterDrawableException(
+ "Missing battery_charge_indicator drawable");
+ }
+ if (level != null) {
+ // check that the level drawable is an AnimatedVectorDrawable
+ if (!(level instanceof AnimatedVectorDrawable)) {
+ throw new BatteryMeterDrawableException("Expected a AnimatedVectorDrawable " +
+ "but received a " + level.getClass().getSimpleName());
+ }
+ // make sure we can stop motion animate the level drawable
+ try {
+ StopMotionVectorDrawable smvd = new StopMotionVectorDrawable(level);
+ smvd.setCurrentFraction(0.5f);
+ } catch (Exception e) {
+ throw new BatteryMeterDrawableException("Unable to perform stop motion on " +
+ "battery_fill drawable", e);
+ }
+ } else {
+ throw new BatteryMeterDrawableException("Missing battery_fill drawable");
+ }
+ }
- private int mAnimOffset;
- private boolean mIsAnimating; // stores charge-animation status to reliably
- //remove callbacks
+ private void loadBatteryDrawables(Resources res, BatteryMeterMode mode) {
+ if (isThemeApplied()) {
+ try {
+ checkBatteryMeterDrawableValid(res, mode);
+ } catch (BatteryMeterDrawableException e) {
+ Log.w(TAG, "Invalid themed battery meter drawable, falling back to system", e);
+ final Context context = getContext();
+ PackageManager pm = getContext().getPackageManager();
+ try {
+ res = pm.getThemedResourcesForApplication(context.getPackageName(),
+ ThemeConfig.SYSTEM_DEFAULT);
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ /* ignore, this should not happen */
+ }
+ }
+ }
- private int mCircleSize; // draw size of circle
- private RectF mRectLeft; // contains the precalculated rect used in drawArc(),
- // derived from mCircleSize
- private float mTextX, mTextY; // precalculated position for drawText() to appear centered
+ int drawableResId = getBatteryDrawableResourceForMode(mode);
+ mBatteryDrawable = (LayerDrawable) res.getDrawable(drawableResId);
+ mFrameDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_frame);
+ mFrameDrawable.setTint(mCurrentBackgroundColor != 0
+ ? mCurrentBackgroundColor
+ : res.getColor(R.color.batterymeter_frame_color));
+ // set the animated vector drawable we will be stop animating
+ Drawable levelDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_fill);
+ mLevelDrawable = new StopMotionVectorDrawable(levelDrawable);
+ mBoltDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator);
+ }
- private Paint mTextPaint;
- private Paint mFrontPaint;
- private Paint mBackPaint;
- private Paint mBoltPaint;
- private Paint mWarningTextPaint;
+ private void drawBattery(Canvas canvas, BatteryTracker tracker) {
+ boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN;
+ int level = tracker.level;
- private final RectF mBoltFrame = new RectF();
+ if (unknownStatus || tracker.status == BatteryManager.BATTERY_STATUS_FULL) {
+ level = 100;
+ }
- private int mChargeColor;
- private final float[] mBoltPoints;
- private final Path mBoltPath = new Path();
+ mTextAndBoltPaint.setColor(getColorForLevel(level));
- public CircleBatteryMeterDrawable(Resources res) {
- super();
- mDisposed = false;
+ // Make sure we don't draw the charge indicator if not plugged in
+ Drawable d = mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator);
+ if (d instanceof BitmapDrawable) {
+ // In case we are using a BitmapDrawable, which we should be unless something bad
+ // happened, we need to change the paint rather than the alpha in case the blendMode
+ // has been set to clear. Clear always clears regardless of alpha level ;)
+ BitmapDrawable bd = (BitmapDrawable) d;
+ bd.getPaint().set(tracker.plugged ? mTextAndBoltPaint : mClearPaint);
+ } else {
+ d.setAlpha(tracker.plugged ? 255 : 0);
+ }
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
- mTextPaint.setTypeface(font);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
-
- mFrontPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mFrontPaint.setStrokeCap(Paint.Cap.BUTT);
- mFrontPaint.setDither(true);
- mFrontPaint.setStrokeWidth(0);
- mFrontPaint.setStyle(Paint.Style.STROKE);
-
- mBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBackPaint.setColor(res.getColor(R.color.batterymeter_frame_color));
- mBackPaint.setStrokeCap(Paint.Cap.BUTT);
- mBackPaint.setDither(true);
- mBackPaint.setStrokeWidth(0);
- mBackPaint.setStyle(Paint.Style.STROKE);
+ // Now draw the level indicator
+ // set the level and tint color of the fill drawable
+ mLevelDrawable.setCurrentFraction(level / 100f);
+ mLevelDrawable.setTint(getColorForLevel(level));
+ mBatteryDrawable.draw(canvas);
- mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mWarningTextPaint.setColor(mColors[1]);
- font = Typeface.create("sans-serif", Typeface.BOLD);
- mWarningTextPaint.setTypeface(font);
- mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
+ // if chosen by options, draw percentage text in the middle
+ // always skip percentage when 100, so layout doesnt break
+ if (unknownStatus) {
+ mTextAndBoltPaint.setColor(getContext().getColor(R.color.batterymeter_frame_color));
+ canvas.drawText("?", mTextX, mTextY, mTextAndBoltPaint);
- mChargeColor = getResources().getColor(R.color.batterymeter_charge_color);
+ } else if (!tracker.plugged) {
+ drawPercentageText(canvas, tracker);
+ }
+ }
- mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color));
- mBoltPoints = loadBoltPoints(res);
+ private void drawPercentageText(Canvas canvas, BatteryTracker tracker) {
+ final int level = tracker.level;
+ if (level > mCriticalLevel
+ && (mShowPercent && !(level == 100 && !SHOW_100_PERCENT))) {
+ // draw the percentage text
+ String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
+ mTextAndBoltPaint.setColor(getColorForLevel(level));
+ canvas.drawText(pctText, mTextX, mTextY, mTextAndBoltPaint);
+ } else if (level <= mCriticalLevel) {
+ // draw the warning text
+ canvas.drawText(mWarningString, mTextX, mTextY, mWarningTextPaint);
+ }
}
- @Override
- public void onDraw(Canvas c, BatteryTracker tracker) {
- if (mDisposed) return;
+ /**
+ * initializes all size dependent variables
+ */
+ private void init() {
+ // not much we can do with zero width or height, we'll get another pass later
+ if (mWidth <= 0 || mHeight <=0) return;
+
+ final float widthDiv2 = mWidth / 2f;
+ // text size is width / 2 - 2dp for wiggle room
+ final float textSize = widthDiv2 - getResources().getDisplayMetrics().density * 2;
+ mTextAndBoltPaint.setTextSize(textSize);
+ mWarningTextPaint.setTextSize(textSize);
+
+ int pLeft = getPaddingLeft();
+ Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight);
+ mBatteryDrawable.setBounds(iconBounds);
- if (mRectLeft == null) {
- initSizeBasedStuff();
+ // calculate text position
+ Rect bounds = new Rect();
+ mTextAndBoltPaint.getTextBounds("99", 0, "99".length(), bounds);
+ boolean isRtl = isLayoutRtl();
+
+ // compute mTextX based on text gravity
+ if ((mTextGravity & Gravity.START) == Gravity.START) {
+ mTextX = isRtl ? mWidth : 0;
+ } else if ((mTextGravity & Gravity.END) == Gravity.END) {
+ mTextX = isRtl ? 0 : mWidth;
+ } else if ((mTextGravity & Gravity.LEFT) == Gravity.LEFT) {
+ mTextX = 0;
+ }else if ((mTextGravity & Gravity.RIGHT) == Gravity.RIGHT) {
+ mTextX = mWidth;
+ } else {
+ mTextX = widthDiv2 + pLeft;
}
- drawCircle(c, tracker, mTextX, mRectLeft);
- if (mAnimationsEnabled) {
- updateChargeAnim(tracker);
+ // compute mTextY based on text gravity
+ if ((mTextGravity & Gravity.TOP) == Gravity.TOP) {
+ mTextY = bounds.height();
+ } else if ((mTextGravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ mTextY = mHeight;
+ } else {
+ mTextY = widthDiv2 + bounds.height() / 2.0f;
}
- }
- @Override
- public void onDispose() {
- mDisposed = true;
+ updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable);
+
+ mInitialized = true;
}
- @Override
- public void setDarkIntensity(int backgroundColor, int fillColor) {
- mIconTint = fillColor;
- mBoltPaint.setColor(fillColor);
- mChargeColor = fillColor;
- invalidate();
+ private int getBatteryDrawableResourceForMode(BatteryMeterMode mode) {
+ switch (mode) {
+ case BATTERY_METER_ICON_LANDSCAPE:
+ return R.drawable.ic_battery_landscape;
+ case BATTERY_METER_CIRCLE:
+ return R.drawable.ic_battery_circle;
+ case BATTERY_METER_ICON_PORTRAIT:
+ return R.drawable.ic_battery_portrait;
+ default:
+ return 0;
+ }
}
- @Override
- public void onSizeChanged(int w, int h, int oldw, int oldh) {
- initSizeBasedStuff();
+ private int getBatteryDrawableStyleResourceForMode(BatteryMeterMode mode) {
+ switch (mode) {
+ case BATTERY_METER_ICON_LANDSCAPE:
+ return R.style.BatteryMeterViewDrawable_Landscape;
+ case BATTERY_METER_CIRCLE:
+ return R.style.BatteryMeterViewDrawable_Circle;
+ case BATTERY_METER_ICON_PORTRAIT:
+ return R.style.BatteryMeterViewDrawable_Portrait;
+ default:
+ return R.style.BatteryMeterViewDrawable;
+ }
}
- private float[] loadBoltPoints(Resources res) {
- final int[] pts = res.getIntArray(getBoltPointsArrayResource());
- int maxX = 0, maxY = 0;
- for (int i = 0; i < pts.length; i += 2) {
- maxX = Math.max(maxX, pts[i]);
- maxY = Math.max(maxY, pts[i + 1]);
+ private Paint.Align getPaintAlignmentFromGravity(int gravity) {
+ boolean isRtl = isLayoutRtl();
+ if ((gravity & Gravity.START) == Gravity.START) {
+ return isRtl ? Paint.Align.RIGHT : Paint.Align.LEFT;
}
- final float[] ptsF = new float[pts.length];
- for (int i = 0; i < pts.length; i += 2) {
- ptsF[i] = (float)pts[i] / maxX;
- ptsF[i + 1] = (float)pts[i + 1] / maxY;
+ if ((gravity & Gravity.END) == Gravity.END) {
+ return isRtl ? Paint.Align.LEFT : Paint.Align.RIGHT;
}
- return ptsF;
- }
+ if ((gravity & Gravity.LEFT) == Gravity.LEFT) return Paint.Align.LEFT;
+ if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) return Paint.Align.RIGHT;
- protected int getBoltPointsArrayResource() {
- return R.array.batterymeter_bolt_points;
+ // default to center
+ return Paint.Align.CENTER;
}
- private void drawCircle(Canvas canvas, BatteryTracker tracker,
- float textX, RectF drawRect) {
- boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN;
- int level = tracker.level;
- Paint paint;
-
- if (unknownStatus) {
- paint = mBackPaint;
- level = 100; // Draw all the circle;
+ // Creates a BitmapDrawable of the bolt so we can make use of the XOR xfer mode with vector
+ // based drawables
+ private void updateBoltDrawableLayer(LayerDrawable batteryDrawable, Drawable boltDrawable) {
+ BitmapDrawable newBoltDrawable;
+ if (boltDrawable instanceof BitmapDrawable) {
+ newBoltDrawable = (BitmapDrawable) boltDrawable.mutate();
} else {
- paint = mFrontPaint;
- paint.setColor(getColorForLevel(level));
- if (tracker.status == BatteryManager.BATTERY_STATUS_FULL) {
- level = 100;
+ Bitmap boltBitmap = createBoltBitmap(boltDrawable);
+ if (boltBitmap == null) {
+ // not much to do with a null bitmap so keep original bolt for now
+ return;
}
+ Rect bounds = boltDrawable.getBounds();
+ newBoltDrawable = new BitmapDrawable(getResources(), boltBitmap);
+ newBoltDrawable.setBounds(bounds);
}
-
- // draw thin gray ring first
- canvas.drawArc(drawRect, 270, 360, false, mBackPaint);
- if (level != 0) {
- // draw colored arc representing charge level
- canvas.drawArc(drawRect, 270 + mAnimOffset, 3.6f * level, false, paint);
- }
- // if chosen by options, draw percentage text in the middle
- // always skip percentage when 100, so layout doesnt break
- if (unknownStatus) {
- mTextPaint.setColor(paint.getColor());
- canvas.drawText("?", textX, mTextY, mTextPaint);
-
- } else if (tracker.plugged) {
- canvas.drawPath(mBoltPath, mBoltPaint);
- } else {
- if (level > mCriticalLevel
- && (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT))) {
- // draw the percentage text
- String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
- mTextPaint.setColor(paint.getColor());
- canvas.drawText(pctText, textX, mTextY, mTextPaint);
- } else if (level <= mCriticalLevel) {
- // draw the warning text
- canvas.drawText(mWarningString, textX, mTextY, mWarningTextPaint);
+ newBoltDrawable.getPaint().set(mTextAndBoltPaint);
+ batteryDrawable.setDrawableByLayerId(R.id.battery_charge_indicator, newBoltDrawable);
+ }
+
+ private Bitmap createBoltBitmap(Drawable boltDrawable) {
+ // not much we can do with zero width or height, we'll get another pass later
+ if (mWidth <= 0 || mHeight <= 0) return null;
+
+ Bitmap bolt;
+ if (!(boltDrawable instanceof BitmapDrawable)) {
+ int pLeft = getPaddingLeft();
+ Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight);
+ bolt = Bitmap.createBitmap(iconBounds.width(), iconBounds.height(),
+ Bitmap.Config.ARGB_8888);
+ if (bolt != null) {
+ Canvas c = new Canvas(bolt);
+ c.drawColor(-1, PorterDuff.Mode.CLEAR);
+ boltDrawable.draw(c);
}
- }
- }
-
- /**
- * updates the animation counter
- * cares for timed callbacks to continue animation cycles
- * uses mInvalidate for delayed invalidate() callbacks
- */
- private void updateChargeAnim(BatteryTracker tracker) {
- // Stop animation when battery is full or after the meter
- // rotated back to 0 after unplugging.
- if (!tracker.shouldIndicateCharging()
- || tracker.status == BatteryManager.BATTERY_STATUS_FULL
- || tracker.level == 0) {
- mIsAnimating = false;
} else {
- mIsAnimating = true;
- }
-
- if (mAnimOffset > 360) {
- mAnimOffset = 0;
- }
-
- boolean continueAnimation = mIsAnimating || mAnimOffset != 0;
-
- if (continueAnimation) {
- mAnimOffset += 3;
+ bolt = ((BitmapDrawable) boltDrawable).getBitmap();
}
- if (continueAnimation) {
- postInvalidateDelayed(50);
- }
+ return bolt;
}
- /**
- * initializes all size dependent variables
- * sets stroke width and text size of all involved paints
- * YES! i think the method name is appropriate
- */
- private void initSizeBasedStuff() {
- mCircleSize = Math.min(getMeasuredWidth(), getMeasuredHeight());
- mTextPaint.setTextSize(mCircleSize / 2f);
- mWarningTextPaint.setTextSize(mCircleSize / 2f);
-
- float strokeWidth = mCircleSize / STROKE_WITH;
- mFrontPaint.setStrokeWidth(strokeWidth);
- mBackPaint.setStrokeWidth(strokeWidth);
-
- // calculate rectangle for drawArc calls
- int pLeft = getPaddingLeft();
- mRectLeft = new RectF(pLeft + strokeWidth / 2.0f, 0 + strokeWidth / 2.0f, mCircleSize
- - strokeWidth / 2.0f + pLeft, mCircleSize - strokeWidth / 2.0f);
+ private class BatteryMeterDrawableException extends RuntimeException {
+ public BatteryMeterDrawableException(String detailMessage) {
+ super(detailMessage);
+ }
- // calculate Y position for text
- Rect bounds = new Rect();
- mTextPaint.getTextBounds("99", 0, "99".length(), bounds);
- mTextX = mCircleSize / 2.0f + getPaddingLeft();
- // the +1dp at end of formula balances out rounding issues.works out on all resolutions
- mTextY = mCircleSize / 2.0f + (bounds.bottom - bounds.top) / 2.0f
- - strokeWidth / 2.0f + getResources().getDisplayMetrics().density;
-
- // draw the bolt
- final float bl = (int) (mRectLeft.left + mRectLeft.width() / 3.2f);
- final float bt = (int) (mRectLeft.top + mRectLeft.height() / 4f);
- final float br = (int) (mRectLeft.right - mRectLeft.width() / 5.2f);
- final float bb = (int) (mRectLeft.bottom - mRectLeft.height() / 8f);
- if (mBoltFrame.left != bl || mBoltFrame.top != bt
- || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
- mBoltFrame.set(bl, bt, br, bb);
- mBoltPath.reset();
- mBoltPath.moveTo(
- mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
- for (int i = 2; i < mBoltPoints.length; i += 2) {
- mBoltPath.lineTo(
- mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
- }
- mBoltPath.lineTo(
- mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
- mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
+ public BatteryMeterDrawableException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
}
}
}