diff options
20 files changed, 713 insertions, 545 deletions
diff --git a/packages/SettingsLib/res/color/batterymeter_bolt_color.xml b/packages/SettingsLib/res/color/batterymeter_bolt_color.xml new file mode 100644 index 000000000000..34de5489a28b --- /dev/null +++ b/packages/SettingsLib/res/color/batterymeter_bolt_color.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="0.3" android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/batterymeter_charge_color.xml b/packages/SettingsLib/res/color/batterymeter_charge_color.xml new file mode 100644 index 000000000000..15944c3a2a07 --- /dev/null +++ b/packages/SettingsLib/res/color/batterymeter_charge_color.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/batterymeter_frame_color.xml b/packages/SettingsLib/res/color/batterymeter_frame_color.xml new file mode 100644 index 000000000000..34de5489a28b --- /dev/null +++ b/packages/SettingsLib/res/color/batterymeter_frame_color.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="0.3" android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml new file mode 100644 index 000000000000..c8a80ac57c00 --- /dev/null +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="0.24" android:color="?android:attr/colorBackground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml new file mode 100644 index 000000000000..8dcfdbb8cf1e --- /dev/null +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="0.47" android:color="?android:attr/colorBackground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml new file mode 100644 index 000000000000..34de5489a28b --- /dev/null +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:alpha="0.3" android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml new file mode 100644 index 000000000000..15944c3a2a07 --- /dev/null +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 7bbca5edbc85..eb64b3a9bbd9 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -520,4 +520,37 @@ <item>7</item> </integer-array> + <!-- BatteryMeterView parameters --> + <array name="batterymeter_color_levels"> + <item>15</item> + <item>100</item> + </array> + <array name="batterymeter_color_values"> + <item>@*android:color/battery_saver_mode_color</item> + <item>@android:color/white</item> + </array> + <array name="batterymeter_bolt_points"> + <item>73</item> <item>0</item> + <item>392</item><item>0</item> + <item>201</item><item>259</item> + <item>442</item><item>259</item> + <item>4</item> <item>703</item> + <item>157</item><item>334</item> + <item>0</item> <item>334</item> + </array> + <array name="batterymeter_plus_points"> + <item>3</item><item>0</item> + <item>5</item><item>0</item> + <item>5</item><item>3</item> + <item>8</item><item>3</item> + <item>8</item><item>5</item> + <item>5</item><item>5</item> + <item>5</item><item>8</item> + <item>3</item><item>8</item> + <item>3</item><item>5</item> + <item>0</item><item>5</item> + <item>0</item><item>3</item> + <item>3</item><item>3</item> + </array> + </resources> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 2e8b30fb5abf..b72bcc71f046 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -57,4 +57,15 @@ <dimen name="drawer_width">300dp</dimen> <dimen name="drawer_item_top_bottom_margin">4dp</dimen> <dimen name="drawer_spacer_height">32dp</dimen> + + <dimen name="battery_height">48dp</dimen> + <dimen name="battery_width">38dp</dimen> + + <!-- Margin on the right side of the system icon group on Keyguard. --> + <fraction name="battery_button_height_fraction">10.5%</fraction> + + <!-- Fraction value to smooth the edges of the battery icon. The path will be inset by this + fraction of a pixel.--> + <fraction name="battery_subpixel_smoothing_left">0%</fraction> + <fraction name="battery_subpixel_smoothing_right">0%</fraction> </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 01296327f689..5475b325773f 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -900,4 +900,7 @@ <string name="retail_demo_reset_next">Next</string> <!-- Title for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=40] --> <string name="retail_demo_reset_title">Password required</string> + + <!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. --> + <string name="battery_meter_very_low_overlay_symbol">!</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java new file mode 100755 index 000000000000..d25bb2e1b1b7 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.graph; + +import android.animation.ArgbEvaluator; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.provider.Settings; +import com.android.settingslib.R; +import com.android.settingslib.Utils; + +public class BatteryMeterDrawableBase extends Drawable { + + private static final float ASPECT_RATIO = 9.5f / 14.5f; + public static final String TAG = BatteryMeterDrawableBase.class.getSimpleName(); + public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; + + protected final Context mContext; + + protected int mLevel = -1; + protected boolean mPluggedIn; + protected boolean mPowerSaveEnabled; + protected boolean mShowPercent; + + private static final boolean SINGLE_DIGIT_PERCENT = false; + + private static final int FULL = 96; + + private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction + + private final int[] mColors; + private final int mIntrinsicWidth; + private final int mIntrinsicHeight; + + private float mButtonHeightFraction; + private float mSubpixelSmoothingLeft; + private float mSubpixelSmoothingRight; + private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint, + mPlusPaint; + private float mTextHeight, mWarningTextHeight; + private int mIconTint = Color.WHITE; + private float mOldDarkIntensity = 0f; + + private int mHeight; + private int mWidth; + private String mWarningString; + private final int mCriticalLevel; + private int mChargeColor; + private final float[] mBoltPoints; + private final Path mBoltPath = new Path(); + private final float[] mPlusPoints; + private final Path mPlusPath = new Path(); + + private final RectF mFrame = new RectF(); + private final RectF mButtonFrame = new RectF(); + private final RectF mBoltFrame = new RectF(); + private final RectF mPlusFrame = new RectF(); + + private final Path mShapePath = new Path(); + private final Path mClipPath = new Path(); + private final Path mTextPath = new Path(); + + private int mDarkModeBackgroundColor; + private int mDarkModeFillColor; + + private int mLightModeBackgroundColor; + private int mLightModeFillColor; + + public BatteryMeterDrawableBase(Context context, int frameColor) { + mContext = context; + final Resources res = context.getResources(); + TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); + TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); + + final int N = levels.length(); + mColors = new int[2 * N]; + for (int i = 0; i < N; i++) { + mColors[2 * i] = levels.getInt(i, 0); + mColors[2 * i + 1] = colors.getColor(i, 0); + } + levels.recycle(); + colors.recycle(); + updateShowPercent(); + mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); + mCriticalLevel = mContext.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); + + mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mFramePaint.setColor(frameColor); + mFramePaint.setDither(true); + mFramePaint.setStrokeWidth(0); + mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); + + mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBatteryPaint.setDither(true); + mBatteryPaint.setStrokeWidth(0); + mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); + mTextPaint.setTypeface(font); + mTextPaint.setTextAlign(Paint.Align.CENTER); + + mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + font = Typeface.create("sans-serif", Typeface.BOLD); + mWarningTextPaint.setTypeface(font); + mWarningTextPaint.setTextAlign(Paint.Align.CENTER); + if (mColors.length > 1) { + mWarningTextPaint.setColor(mColors[1]); + } + + mChargeColor = Utils.getDefaultColor(mContext, R.color.batterymeter_charge_color); + + mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBoltPaint.setColor(Utils.getDefaultColor(mContext, R.color.batterymeter_bolt_color)); + mBoltPoints = loadBoltPoints(res); + + mPlusPaint = new Paint(mBoltPaint); + mPlusPoints = loadPlusPoints(res); + + mDarkModeBackgroundColor = + Utils.getDefaultColor(mContext, R.color.dark_mode_icon_color_dual_tone_background); + mDarkModeFillColor = + Utils.getDefaultColor(mContext, R.color.dark_mode_icon_color_dual_tone_fill); + mLightModeBackgroundColor = + Utils.getDefaultColor(mContext, R.color.light_mode_icon_color_dual_tone_background); + mLightModeFillColor = + Utils.getDefaultColor(mContext, R.color.light_mode_icon_color_dual_tone_fill); + + mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width); + mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height); + } + + @Override + public int getIntrinsicHeight() { + return mIntrinsicHeight; + } + + @Override + public int getIntrinsicWidth() { + return mIntrinsicWidth; + } + + public void disableShowPercent() { + mShowPercent = false; + postInvalidate(); + } + + protected void postInvalidate() { + scheduleSelf(this::invalidateSelf, 0); + } + + private static float[] loadBoltPoints(Resources res) { + final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points); + 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; + } + + private static float[] loadPlusPoints(Resources res) { + final int[] pts = res.getIntArray(R.array.batterymeter_plus_points); + 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; + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + mHeight = bottom - top; + mWidth = right - left; + mWarningTextPaint.setTextSize(mHeight * 0.75f); + mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; + } + + protected void updateShowPercent() { + mShowPercent = true; + } + + private int getColorForLevel(int percent) { + // If we are in power save mode, always use the normal color. + if (mPowerSaveEnabled) { + return mColors[mColors.length - 1]; + } + int thresh, color = 0; + for (int i = 0; i < mColors.length; i += 2) { + thresh = mColors[i]; + color = mColors[i + 1]; + if (percent <= thresh) { + + // Respect tinting for "normal" level + if (i == mColors.length - 2) { + return mIconTint; + } else { + return color; + } + } + } + return color; + } + + public void setDarkIntensity(float darkIntensity) { + if (darkIntensity == mOldDarkIntensity) { + return; + } + int backgroundColor = getBackgroundColor(darkIntensity); + int fillColor = getFillColor(darkIntensity); + setColors(fillColor, backgroundColor); + mOldDarkIntensity = darkIntensity; + } + + public void setColors(int fillColor, int backgroundColor) { + mIconTint = fillColor; + mFramePaint.setColor(backgroundColor); + mBoltPaint.setColor(fillColor); + mChargeColor = fillColor; + invalidateSelf(); + } + + private int getBackgroundColor(float darkIntensity) { + return getColorForDarkIntensity( + darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor); + } + + private int getFillColor(float darkIntensity) { + return getColorForDarkIntensity( + darkIntensity, mLightModeFillColor, mDarkModeFillColor); + } + + private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { + return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); + } + + @Override + public void draw(Canvas c) { + final int level = mLevel; + + if (level == -1) return; + + float drawFrac = (float) level / 100f; + final int height = mHeight; + final int width = (int) (ASPECT_RATIO * mHeight); + int px = (mWidth - width) / 2; + + final int buttonHeight = (int) (height * mButtonHeightFraction); + + mFrame.set(0, 0, width, height); + mFrame.offset(px, 0); + + // 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 + mFrame.top += buttonHeight; + mFrame.left += mSubpixelSmoothingLeft; + mFrame.top += mSubpixelSmoothingLeft; + mFrame.right -= mSubpixelSmoothingRight; + mFrame.bottom -= mSubpixelSmoothingRight; + + // set the battery charging color + mBatteryPaint.setColor(mPluggedIn ? mChargeColor : getColorForLevel(level)); + + if (level >= FULL) { + drawFrac = 1f; + } else if (level <= mCriticalLevel) { + drawFrac = 0f; + } + + final float levelTop = drawFrac == 1f ? mButtonFrame.top + : (mFrame.top + (mFrame.height() * (1f - drawFrac))); + + // define the battery shape + mShapePath.reset(); + mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); + 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 (mPluggedIn) { + // define the bolt shape + final float bl = mFrame.left + mFrame.width() / 4f; + final float bt = mFrame.top + mFrame.height() / 6f; + final float br = mFrame.right - mFrame.width() / 4f; + final float bb = mFrame.bottom - mFrame.height() / 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 = (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); + } + } else if (mPowerSaveEnabled) { + // define the plus shape + final float pw = mFrame.width() * 2 / 3; + final float pl = mFrame.left + (mFrame.width() - pw) / 2; + final float pt = mFrame.top + (mFrame.height() - pw) / 2; + final float pr = mFrame.right - (mFrame.width() - pw) / 2; + final float pb = mFrame.bottom - (mFrame.height() - pw) / 2; + if (mPlusFrame.left != pl || mPlusFrame.top != pt + || mPlusFrame.right != pr || mPlusFrame.bottom != pb) { + mPlusFrame.set(pl, pt, pr, pb); + mPlusPath.reset(); + mPlusPath.moveTo( + mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(), + mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height()); + for (int i = 2; i < mPlusPoints.length; i += 2) { + mPlusPath.lineTo( + mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(), + mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height()); + } + mPlusPath.lineTo( + mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(), + mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height()); + } + + float boltPct = (mPlusFrame.bottom - levelTop) / (mPlusFrame.bottom - mPlusFrame.top); + boltPct = Math.min(Math.max(boltPct, 0), 1); + if (boltPct <= BOLT_LEVEL_THRESHOLD) { + // draw the bolt if opaque + c.drawPath(mPlusPath, mPlusPaint); + } else { + // otherwise cut the bolt out of the overall shape + mShapePath.op(mPlusPath, Path.Op.DIFFERENCE); + } + } + + // compute percentage text + boolean pctOpaque = false; + float pctX = 0, pctY = 0; + String pctText = null; + if (!mPluggedIn && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) { + mTextPaint.setColor(getColorForLevel(level)); + mTextPaint.setTextSize(height * + (SINGLE_DIGIT_PERCENT ? 0.75f + : (mLevel == 100 ? 0.38f : 0.5f))); + mTextHeight = -mTextPaint.getFontMetrics().ascent; + pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level); + pctX = mWidth * 0.5f; + pctY = (mHeight + mTextHeight) * 0.47f; + 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 + mFrame.top = levelTop; + mClipPath.reset(); + mClipPath.addRect(mFrame, Path.Direction.CCW); + mShapePath.op(mClipPath, Path.Op.INTERSECT); + c.drawPath(mShapePath, mBatteryPaint); + + if (!mPluggedIn && !mPowerSaveEnabled) { + 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); + } + } + } + + // Some stuff required by Drawable. + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + mFramePaint.setColorFilter(colorFilter); + mBatteryPaint.setColorFilter(colorFilter); + mWarningTextPaint.setColorFilter(colorFilter); + mBoltPaint.setColorFilter(colorFilter); + mPlusPaint.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return 0; + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java new file mode 100644 index 000000000000..fab00dab5be7 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java @@ -0,0 +1,51 @@ +package com.android.settingslib.graph; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import com.android.settingslib.R; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatteryMeterDrawableBaseTest { + private Context mContext; + private Resources mResources; + private BatteryMeterDrawableBase mBatteryDrawable; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mResources = mContext.getResources(); + mBatteryDrawable = new BatteryMeterDrawableBase(mContext, 0); + } + + @Test + public void testGetIntrinsicSize() { + assertThat(mBatteryDrawable.getIntrinsicWidth()). + isEqualTo(mResources.getDimensionPixelSize(R.dimen.battery_width)); + assertThat(mBatteryDrawable.getIntrinsicHeight()). + isEqualTo(mResources.getDimensionPixelSize(R.dimen.battery_height)); + } + + @Test + public void testDrawNothingBeforeOnBatteryLevelChanged() { + final Canvas canvas = mock(Canvas.class); + mBatteryDrawable.draw(canvas); + verify(canvas, never()).drawPath(any(), any()); + verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any()); + } +} diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml deleted file mode 100644 index bf0cba22ab0c..000000000000 --- a/packages/SystemUI/res/values/arrays.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/assets/res/any/colors.xml -** -** Copyright 2012, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - - <!-- BatteryMeterView parameters --> - <array name="batterymeter_color_levels"> - <item>15</item> - <item>100</item> - </array> - <array name="batterymeter_color_values"> - <item>@*android:color/battery_saver_mode_color</item> - <item>#FFFFFFFF</item> - </array> - <array name="batterymeter_bolt_points"> - <item>73</item> <item>0</item> - <item>392</item><item>0</item> - <item>201</item><item>259</item> - <item>442</item><item>259</item> - <item>4</item> <item>703</item> - <item>157</item><item>334</item> - <item>0</item> <item>334</item> - </array> - <array name="batterymeter_plus_points"> - <item>3</item><item>0</item> - <item>5</item><item>0</item> - <item>5</item><item>3</item> - <item>8</item><item>3</item> - <item>8</item><item>5</item> - <item>5</item><item>5</item> - <item>5</item><item>8</item> - <item>3</item><item>8</item> - <item>3</item><item>5</item> - <item>0</item><item>5</item> - <item>0</item><item>3</item> - <item>3</item><item>3</item> - </array> -</resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index ad5b108f62b5..1249f44cfabf 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -26,9 +26,6 @@ <drawable name="status_bar_notification_row_background_color">#ff090909</drawable> <color name="notification_list_shadow_top">#80000000</color> <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable> - <color name="batterymeter_frame_color">#4DFFFFFF</color><!-- 30% white --> - <color name="batterymeter_charge_color">#FFFFFFFF</color> - <color name="batterymeter_bolt_color">#FFFFFFFF</color> <color name="qs_batterymeter_frame_color">#FF404040</color> <color name="system_warning_color">@*android:color/system_error</color> <color name="qs_tile_divider">#29ffffff</color><!-- 16% white --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b768f9086675..e69c4a3ed22b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -520,14 +520,6 @@ <!-- How much two taps can be apart to still be recognized as a double tap on the lockscreen --> <dimen name="double_tap_slop">32dp</dimen> - <!-- Margin on the right side of the system icon group on Keyguard. --> - <fraction name="battery_button_height_fraction">10.5%</fraction> - - <!-- Fraction value to smooth the edges of the battery icon. The path will be inset by this - fraction of a pixel.--> - <fraction name="battery_subpixel_smoothing_left">0%</fraction> - <fraction name="battery_subpixel_smoothing_right">0%</fraction> - <dimen name="battery_margin_bottom">0dp</dimen> <!-- Padding at the end of the view that displays the mobile signal icons. If the view is @@ -642,9 +634,6 @@ <dimen name="docked_divider_handle_width">16dp</dimen> <dimen name="docked_divider_handle_height">2dp</dimen> - <dimen name="battery_height">14.5dp</dimen> - <dimen name="battery_width">9.5dp</dimen> - <dimen name="battery_detail_graph_space_top">27dp</dimen> <dimen name="battery_detail_graph_space_bottom">27dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8553952b692d..f7cf444d2350 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -819,9 +819,6 @@ <!-- Expanded Status Bar Header: Not charging [CHAR LIMIT=40] --> <string name="expanded_header_battery_not_charging">Not charging</string> - <!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. --> - <string name="battery_meter_very_low_overlay_symbol">!</string> - <!-- Shows up when there is a user SSL CA Cert installed on the device. Indicates to the user that SSL traffic can be intercepted. If the text fits on one line (~14 chars), it should start with a diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java index 5a6afca627ec..2bdb2f33a37e 100755..100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java @@ -16,193 +16,24 @@ package com.android.systemui; -import android.animation.ArgbEvaluator; -import android.annotation.Nullable; import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; import android.database.ContentObserver; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.provider.Settings; - +import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.systemui.statusbar.policy.BatteryController; -public class BatteryMeterDrawable extends Drawable implements +public class BatteryMeterDrawable extends BatteryMeterDrawableBase implements BatteryController.BatteryStateChangeCallback { - private static final float ASPECT_RATIO = 9.5f / 14.5f; - public static final String TAG = BatteryMeterDrawable.class.getSimpleName(); - public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; - - private static final boolean SINGLE_DIGIT_PERCENT = false; - - private static final int FULL = 96; - - private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction - - private final int[] mColors; - private final int mIntrinsicWidth; - private final int mIntrinsicHeight; - - private boolean mShowPercent; - private float mButtonHeightFraction; - private float mSubpixelSmoothingLeft; - private float mSubpixelSmoothingRight; - private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint, - mPlusPaint; - private float mTextHeight, mWarningTextHeight; - private int mIconTint = Color.WHITE; - private float mOldDarkIntensity = 0f; - - private int mHeight; - private int mWidth; - private String mWarningString; - private final int mCriticalLevel; - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); - private final float[] mPlusPoints; - private final Path mPlusPath = new Path(); - - private final RectF mFrame = new RectF(); - private final RectF mButtonFrame = new RectF(); - private final RectF mBoltFrame = new RectF(); - private final RectF mPlusFrame = new RectF(); - - private final Path mShapePath = new Path(); - private final Path mClipPath = new Path(); - private final Path mTextPath = new Path(); - private BatteryController mBatteryController; - private boolean mPowerSaveEnabled; - - private int mDarkModeBackgroundColor; - private int mDarkModeFillColor; - - private int mLightModeBackgroundColor; - private int mLightModeFillColor; - - private final SettingObserver mSettingObserver; - - private final Context mContext; - - private int mLevel = -1; - private boolean mPluggedIn; - private boolean mListening; + private SettingObserver mSettingObserver; public BatteryMeterDrawable(Context context, int frameColor) { - mContext = context; - mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper())); - final Resources res = context.getResources(); - TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); - TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); - - final int N = levels.length(); - mColors = new int[2*N]; - for (int i=0; i<N; i++) { - mColors[2*i] = levels.getInt(i, 0); - mColors[2*i+1] = colors.getColor(i, 0); - } - levels.recycle(); - colors.recycle(); - updateShowPercent(); - mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); - mCriticalLevel = mContext.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); - - mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFramePaint.setColor(frameColor); - mFramePaint.setDither(true); - mFramePaint.setStrokeWidth(0); - mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); - - mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBatteryPaint.setDither(true); - mBatteryPaint.setStrokeWidth(0); - mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); - - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); - - 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 = context.getColor(R.color.batterymeter_charge_color); - - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); - - mPlusPaint = new Paint(mBoltPaint); - mPlusPoints = loadPlusPoints(res); - - mDarkModeBackgroundColor = - context.getColor(R.color.dark_mode_icon_color_dual_tone_background); - mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill); - mLightModeBackgroundColor = - context.getColor(R.color.light_mode_icon_color_dual_tone_background); - mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill); - - mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width); - mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height); - } - - @Override - public int getIntrinsicHeight() { - return mIntrinsicHeight; - } - - @Override - public int getIntrinsicWidth() { - return mIntrinsicWidth; - } - - public void startListening() { - mListening = true; - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); - updateShowPercent(); - mBatteryController.addCallback(this); - } - - public void stopListening() { - mListening = false; - mContext.getContentResolver().unregisterContentObserver(mSettingObserver); - mBatteryController.removeCallback(this); - } + super(context, frameColor); - public void disableShowPercent() { - mShowPercent = false; - postInvalidate(); - } - - private void postInvalidate() { - scheduleSelf(this::invalidateSelf, 0); - } - - public void setBatteryController(BatteryController batteryController) { - mBatteryController = batteryController; - mPowerSaveEnabled = mBatteryController.isPowerSave(); + mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper())); } @Override @@ -219,293 +50,27 @@ public class BatteryMeterDrawable extends Drawable implements invalidateSelf(); } - private static float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points); - 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; + public void startListening() { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); + updateShowPercent(); + mBatteryController.addCallback(this); } - private static float[] loadPlusPoints(Resources res) { - final int[] pts = res.getIntArray(R.array.batterymeter_plus_points); - 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; + public void stopListening() { + mContext.getContentResolver().unregisterContentObserver(mSettingObserver); + mBatteryController.removeCallback(this); } @Override - public void setBounds(int left, int top, int right, int bottom) { - super.setBounds(left, top, right, bottom); - mHeight = bottom - top; - mWidth = right - left; - mWarningTextPaint.setTextSize(mHeight * 0.75f); - mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; - } - - private void updateShowPercent() { + protected void updateShowPercent() { mShowPercent = 0 != Settings.System.getInt(mContext.getContentResolver(), SHOW_PERCENT_SETTING, 0); } - private int getColorForLevel(int percent) { - - // If we are in power save mode, always use the normal color. - if (mPowerSaveEnabled) { - return mColors[mColors.length-1]; - } - int thresh, color = 0; - for (int i=0; i<mColors.length; i+=2) { - thresh = mColors[i]; - color = mColors[i+1]; - if (percent <= thresh) { - - // Respect tinting for "normal" level - if (i == mColors.length-2) { - return mIconTint; - } else { - return color; - } - } - } - return color; - } - - public void setDarkIntensity(float darkIntensity) { - if (darkIntensity == mOldDarkIntensity) { - return; - } - int backgroundColor = getBackgroundColor(darkIntensity); - int fillColor = getFillColor(darkIntensity); - setColors(fillColor, backgroundColor); - mOldDarkIntensity = darkIntensity; - } - - public void setColors(int fillColor, int backgroundColor) { - mIconTint = fillColor; - mFramePaint.setColor(backgroundColor); - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; - invalidateSelf(); - } - - private int getBackgroundColor(float darkIntensity) { - return getColorForDarkIntensity( - darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor); - } - - private int getFillColor(float darkIntensity) { - return getColorForDarkIntensity( - darkIntensity, mLightModeFillColor, mDarkModeFillColor); - } - - private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { - return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); - } - - @Override - public void draw(Canvas c) { - final int level = mLevel; - - if (level == -1) return; - - float drawFrac = (float) level / 100f; - final int height = mHeight; - final int width = (int) (ASPECT_RATIO * mHeight); - int px = (mWidth - width) / 2; - - final int buttonHeight = (int) (height * mButtonHeightFraction); - - mFrame.set(0, 0, width, height); - mFrame.offset(px, 0); - - // 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 - mFrame.top += buttonHeight; - mFrame.left += mSubpixelSmoothingLeft; - mFrame.top += mSubpixelSmoothingLeft; - mFrame.right -= mSubpixelSmoothingRight; - mFrame.bottom -= mSubpixelSmoothingRight; - - // set the battery charging color - mBatteryPaint.setColor(mPluggedIn ? mChargeColor : getColorForLevel(level)); - - if (level >= FULL) { - drawFrac = 1f; - } else if (level <= mCriticalLevel) { - drawFrac = 0f; - } - - final float levelTop = drawFrac == 1f ? mButtonFrame.top - : (mFrame.top + (mFrame.height() * (1f - drawFrac))); - - // define the battery shape - mShapePath.reset(); - mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); - 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 (mPluggedIn) { - // define the bolt shape - final float bl = mFrame.left + mFrame.width() / 4f; - final float bt = mFrame.top + mFrame.height() / 6f; - final float br = mFrame.right - mFrame.width() / 4f; - final float bb = mFrame.bottom - mFrame.height() / 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 = (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); - } - } else if (mPowerSaveEnabled) { - // define the plus shape - final float pw = mFrame.width() * 2 / 3; - final float pl = mFrame.left + (mFrame.width() - pw) / 2; - final float pt = mFrame.top + (mFrame.height() - pw) / 2; - final float pr = mFrame.right - (mFrame.width() - pw) / 2; - final float pb = mFrame.bottom - (mFrame.height() - pw) / 2; - if (mPlusFrame.left != pl || mPlusFrame.top != pt - || mPlusFrame.right != pr || mPlusFrame.bottom != pb) { - mPlusFrame.set(pl, pt, pr, pb); - mPlusPath.reset(); - mPlusPath.moveTo( - mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(), - mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height()); - for (int i = 2; i < mPlusPoints.length; i += 2) { - mPlusPath.lineTo( - mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(), - mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height()); - } - mPlusPath.lineTo( - mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(), - mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height()); - } - - float boltPct = (mPlusFrame.bottom - levelTop) / (mPlusFrame.bottom - mPlusFrame.top); - boltPct = Math.min(Math.max(boltPct, 0), 1); - if (boltPct <= BOLT_LEVEL_THRESHOLD) { - // draw the bolt if opaque - c.drawPath(mPlusPath, mPlusPaint); - } else { - // otherwise cut the bolt out of the overall shape - mShapePath.op(mPlusPath, Path.Op.DIFFERENCE); - } - } - - // compute percentage text - boolean pctOpaque = false; - float pctX = 0, pctY = 0; - String pctText = null; - if (!mPluggedIn && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) { - mTextPaint.setColor(getColorForLevel(level)); - mTextPaint.setTextSize(height * - (SINGLE_DIGIT_PERCENT ? 0.75f - : (mLevel == 100 ? 0.38f : 0.5f))); - mTextHeight = -mTextPaint.getFontMetrics().ascent; - pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - pctX = mWidth * 0.5f; - pctY = (mHeight + mTextHeight) * 0.47f; - 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 - mFrame.top = levelTop; - mClipPath.reset(); - mClipPath.addRect(mFrame, Path.Direction.CCW); - mShapePath.op(mClipPath, Path.Op.INTERSECT); - c.drawPath(mShapePath, mBatteryPaint); - - if (!mPluggedIn && !mPowerSaveEnabled) { - 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); - } - } - } - - // Some stuff required by Drawable. - @Override - public void setAlpha(int alpha) { - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - mFramePaint.setColorFilter(colorFilter); - mBatteryPaint.setColorFilter(colorFilter); - mWarningTextPaint.setColorFilter(colorFilter); - mTextPaint.setColorFilter(colorFilter); - mBoltPaint.setColorFilter(colorFilter); - mPlusPaint.setColorFilter(colorFilter); - } - - @Override - public int getOpacity() { - return 0; + public void setBatteryController(BatteryController batteryController) { + mBatteryController = batteryController; + mPowerSaveEnabled = mBatteryController.isPowerSave(); } private final class SettingObserver extends ContentObserver { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java index 3058c0a1e739..ee0116ec7dc6 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java @@ -25,7 +25,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.statusbar.phone.StatusBarIconController; -import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING; +import static com.android.settingslib.graph.BatteryMeterDrawableBase.SHOW_PERCENT_SETTING; public class BatteryPreference extends DropDownPreference implements TunerService.Tunable { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index ca582b360a48..7acd888483ab 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -38,7 +38,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.systemui.BatteryMeterDrawable; +import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -200,7 +200,8 @@ public class TunerService { public void clearAll() { // A couple special cases. Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); - Settings.System.putString(mContentResolver, BatteryMeterDrawable.SHOW_PERCENT_SETTING, null); + Settings.System.putString(mContentResolver, + BatteryMeterDrawableBase.SHOW_PERCENT_SETTING, null); Intent intent = new Intent(DemoMode.ACTION_DEMO); intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); mContext.sendBroadcast(intent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java index cb0f7a388d01..09808d47a995 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java @@ -16,7 +16,6 @@ package com.android.systemui; -import static junit.framework.Assert.assertEquals; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyFloat; @@ -51,24 +50,6 @@ public class BatteryMeterDrawableTest extends SysuiTestCase { } @Test - public void testGetIntrinsicSize() { - assertEquals( - mResources.getDimensionPixelSize(R.dimen.battery_width), - mBatteryMeter.getIntrinsicWidth()); - assertEquals( - mResources.getDimensionPixelSize(R.dimen.battery_height), - mBatteryMeter.getIntrinsicHeight()); - } - - @Test - public void testDrawNothingBeforeOnBatteryLevelChanged() { - final Canvas canvas = mock(Canvas.class); - mBatteryMeter.draw(canvas); - verify(canvas, never()).drawPath(any(), any()); - verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any()); - } - - @Test public void testDrawImageButNoTextIfPluggedIn() { mBatteryMeter.onBatteryLevelChanged(0, true, true); final Canvas canvas = mock(Canvas.class); |