Revamp battery Ui in low battery mode

This cl refine battery icon ui in low battery mode:
1. Should show indicator(fixed in ag/2035178)
2. Show bolt icon when charging
3. When battery is low, set battery background as red and indicator
as white.

This cl also includes the field updates for battery.

Bug: 36862496
Test: RunSettingsRoboTests
Change-Id: Ifb2ed339742119bbff78712df09288b895756b1f
This commit is contained in:
jackqdyulei
2017-04-04 10:03:14 -07:00
parent 66871d8531
commit f8e6f43425
7 changed files with 138 additions and 27 deletions

View File

@@ -0,0 +1,19 @@
<?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="@*android:dimen/secondary_content_alpha_material_dark"
android:color="?android:attr/colorError"/>
</selector>

View File

@@ -511,7 +511,7 @@ public class BatteryHistoryChart extends View {
elapsedRealtimeUs); elapsedRealtimeUs);
mDrainString = ""; mDrainString = "";
mChargeDurationString = ""; mChargeDurationString = "";
setContentDescription(mInfo.mChargeLabelString); setContentDescription(mInfo.chargeLabelString);
int pos = 0; int pos = 0;
int lastInteresting = 0; int lastInteresting = 0;
@@ -589,7 +589,7 @@ public class BatteryHistoryChart extends View {
mMaxPercentLabelStringWidth = (int)mTextPaint.measureText(mMaxPercentLabelString); mMaxPercentLabelStringWidth = (int)mTextPaint.measureText(mMaxPercentLabelString);
mMinPercentLabelStringWidth = (int)mTextPaint.measureText(mMinPercentLabelString); mMinPercentLabelStringWidth = (int)mTextPaint.measureText(mMinPercentLabelString);
mDrainStringWidth = (int)mHeaderTextPaint.measureText(mDrainString); mDrainStringWidth = (int)mHeaderTextPaint.measureText(mDrainString);
mChargeLabelStringWidth = (int)mHeaderTextPaint.measureText(mInfo.mChargeLabelString); mChargeLabelStringWidth = (int)mHeaderTextPaint.measureText(mInfo.chargeLabelString);
mChargeDurationStringWidth = (int)mHeaderTextPaint.measureText(mChargeDurationString); mChargeDurationStringWidth = (int)mHeaderTextPaint.measureText(mChargeDurationString);
mTextAscent = (int)mTextPaint.ascent(); mTextAscent = (int)mTextPaint.ascent();
mTextDescent = (int)mTextPaint.descent(); mTextDescent = (int)mTextPaint.descent();
@@ -974,7 +974,7 @@ public class BatteryHistoryChart extends View {
} }
} }
} }
i++; i++;
} }
mStats.finishIteratingHistoryLocked(); mStats.finishIteratingHistoryLocked();
@@ -983,9 +983,9 @@ public class BatteryHistoryChart extends View {
if (lastY < 0 || lastX < 0) { if (lastY < 0 || lastX < 0) {
// Didn't get any data... // Didn't get any data...
x = lastX = mLevelLeft; x = lastX = mLevelLeft;
y = lastY = mLevelTop + levelh - ((mInfo.mBatteryLevel-batLow)*(levelh-1))/batChange; y = lastY = mLevelTop + levelh - ((mInfo.batteryLevel -batLow)*(levelh-1))/batChange;
Path path; Path path;
byte value = (byte)mInfo.mBatteryLevel; byte value = (byte)mInfo.batteryLevel;
if (value <= mBatteryCriticalLevel) path = mBatCriticalPath; if (value <= mBatteryCriticalLevel) path = mBatCriticalPath;
else if (value <= mBatteryWarnLevel) path = mBatWarnPath; else if (value <= mBatteryWarnLevel) path = mBatWarnPath;
else path = null; //mBatGoodPath; else path = null; //mBatGoodPath;
@@ -1014,7 +1014,7 @@ public class BatteryHistoryChart extends View {
mTimeRemainPath.moveTo(x, lastY); mTimeRemainPath.moveTo(x, lastY);
int fullY = mLevelTop + levelh - ((100-batLow)*(levelh-1))/batChange; int fullY = mLevelTop + levelh - ((100-batLow)*(levelh-1))/batChange;
int emptyY = mLevelTop + levelh - ((0-batLow)*(levelh-1))/batChange; int emptyY = mLevelTop + levelh - ((0-batLow)*(levelh-1))/batChange;
if (mInfo.mDischarging) { if (mInfo.discharging) {
mTimeRemainPath.lineTo(mLevelRight, emptyY); mTimeRemainPath.lineTo(mLevelRight, emptyY);
} else { } else {
mTimeRemainPath.lineTo(mLevelRight, fullY); mTimeRemainPath.lineTo(mLevelRight, fullY);
@@ -1211,8 +1211,8 @@ public class BatteryHistoryChart extends View {
int headerTop = -mHeaderTextAscent + (mHeaderTextDescent-mHeaderTextAscent)/3; int headerTop = -mHeaderTextAscent + (mHeaderTextDescent-mHeaderTextAscent)/3;
mHeaderTextPaint.setTextAlign(textAlignLeft); mHeaderTextPaint.setTextAlign(textAlignLeft);
if (DEBUG) Log.d(TAG, "Drawing charge label string: " + mInfo.mChargeLabelString); if (DEBUG) Log.d(TAG, "Drawing charge label string: " + mInfo.chargeLabelString);
canvas.drawText(mInfo.mChargeLabelString, textStartX, headerTop, mHeaderTextPaint); canvas.drawText(mInfo.chargeLabelString, textStartX, headerTop, mHeaderTextPaint);
int stringHalfWidth = mChargeDurationStringWidth / 2; int stringHalfWidth = mChargeDurationStringWidth / 2;
if (layoutRtl) stringHalfWidth = -stringHalfWidth; if (layoutRtl) stringHalfWidth = -stringHalfWidth;
int headerCenter = ((width-mChargeDurationStringWidth-mDrainStringWidth)/2) int headerCenter = ((width-mChargeDurationStringWidth-mDrainStringWidth)/2)

View File

@@ -18,17 +18,25 @@ package com.android.settings.fuelgauge;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.ImageView; import android.widget.ImageView;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.settingslib.graph.BatteryMeterDrawableBase;
public class BatteryMeterView extends ImageView { public class BatteryMeterView extends ImageView {
private BatteryMeterDrawable mDrawable; @VisibleForTesting
BatteryMeterDrawable mDrawable;
@VisibleForTesting
ColorFilter mErrorColorFilter;
@VisibleForTesting
ColorFilter mAccentColorFilter;
public BatteryMeterView(Context context) { public BatteryMeterView(Context context) {
this(context, null, 0); this(context, null, 0);
@@ -42,21 +50,30 @@ public class BatteryMeterView extends ImageView {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
final int frameColor = context.getColor(R.color.batterymeter_frame_color); final int frameColor = context.getColor(R.color.batterymeter_frame_color);
final int tintColor = Utils.getColorAttr(context, android.R.attr.colorAccent); mAccentColorFilter = new PorterDuffColorFilter(
Utils.getColorAttr(context, android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN);
mErrorColorFilter = new PorterDuffColorFilter(
context.getColor(R.color.battery_icon_color_error), PorterDuff.Mode.SRC_IN);
mDrawable = new BatteryMeterDrawable(context, frameColor); mDrawable = new BatteryMeterDrawable(context, frameColor);
mDrawable.setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN));
mDrawable.setShowPercent(false); mDrawable.setShowPercent(false);
mDrawable.setBatteryColorFilter(mAccentColorFilter);
mDrawable.setWarningColorFilter(
new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN));
setImageDrawable(mDrawable); setImageDrawable(mDrawable);
} }
public void setBatteryInfo(int level) { public void setBatteryLevel(int level) {
mDrawable.setBatteryLevel(level); mDrawable.setBatteryLevel(level);
if (level < mDrawable.getCriticalLevel()) {
mDrawable.setBatteryColorFilter(mErrorColorFilter);
} else {
mDrawable.setBatteryColorFilter(mAccentColorFilter);
}
} }
@VisibleForTesting public void setCharging(boolean charging) {
void setBatteryDrawable(BatteryMeterDrawable drawable) { mDrawable.setCharging(charging);
mDrawable = drawable;
} }
public static class BatteryMeterDrawable extends BatteryMeterDrawableBase { public static class BatteryMeterDrawable extends BatteryMeterDrawableBase {
@@ -81,6 +98,16 @@ public class BatteryMeterView extends ImageView {
public int getIntrinsicHeight() { public int getIntrinsicHeight() {
return mIntrinsicHeight; return mIntrinsicHeight;
} }
public void setWarningColorFilter(@Nullable ColorFilter colorFilter) {
mWarningTextPaint.setColorFilter(colorFilter);
}
public void setBatteryColorFilter(@Nullable ColorFilter colorFilter) {
mFramePaint.setColorFilter(colorFilter);
mBatteryPaint.setColorFilter(colorFilter);
mBoltPaint.setColorFilter(colorFilter);
}
} }
} }

View File

@@ -568,14 +568,15 @@ public class PowerUsageSummary extends PowerUsageBase {
.findViewById(R.id.battery_header_icon); .findViewById(R.id.battery_header_icon);
final TextView timeText = (TextView) mBatteryLayoutPref.findViewById(R.id.battery_percent); final TextView timeText = (TextView) mBatteryLayoutPref.findViewById(R.id.battery_percent);
final TextView summary1 = (TextView) mBatteryLayoutPref.findViewById(R.id.summary1); final TextView summary1 = (TextView) mBatteryLayoutPref.findViewById(R.id.summary1);
timeText.setText(Utils.formatPercentage(info.mBatteryLevel)); timeText.setText(Utils.formatPercentage(info.batteryLevel));
if (info.remainingLabel == null ) { if (info.remainingLabel == null ) {
summary1.setText(info.statusLabel); summary1.setText(info.statusLabel);
} else { } else {
summary1.setText(info.remainingLabel); summary1.setText(info.remainingLabel);
} }
batteryView.setBatteryInfo(info.mBatteryLevel); batteryView.setBatteryLevel(info.batteryLevel);
batteryView.setCharging(!info.discharging);
} }
@VisibleForTesting @VisibleForTesting
@@ -736,7 +737,7 @@ public class PowerUsageSummary extends PowerUsageBase {
BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() { BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
@Override @Override
public void onBatteryInfoLoaded(BatteryInfo info) { public void onBatteryInfoLoaded(BatteryInfo info) {
mLoader.setSummary(SummaryProvider.this, info.mChargeLabelString); mLoader.setSummary(SummaryProvider.this, info.chargeLabelString);
} }
}); });
} }

View File

@@ -15,12 +15,17 @@
*/ */
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context; import android.content.Context;
import android.graphics.ColorFilter;
import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme; import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor; import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -29,6 +34,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@@ -42,10 +49,15 @@ import static org.mockito.Mockito.verify;
}) })
public class BatteryMeterViewTest { public class BatteryMeterViewTest {
private static final int BATTERY_LEVEL = 100; private static final int BATTERY_LEVEL = 100;
private static final int BATTERY_CRITICAL_LEVEL = 15;
private static final int BATTERY_LOW_LEVEL = 3;
@Mock @Mock
private BatteryMeterView.BatteryMeterDrawable mDrawable; private ColorFilter mErrorColorFilter;
@Mock
private ColorFilter mAccentColorFilter;
private Context mContext; private Context mContext;
private BatteryMeterView mBatteryMeterView; private BatteryMeterView mBatteryMeterView;
private BatteryMeterView.BatteryMeterDrawable mDrawable;
@Before @Before
public void setUp() { public void setUp() {
@@ -53,13 +65,33 @@ public class BatteryMeterViewTest {
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mBatteryMeterView = new BatteryMeterView(mContext); mBatteryMeterView = new BatteryMeterView(mContext);
mBatteryMeterView.setBatteryDrawable(mDrawable); mDrawable = spy(new BatteryMeterView.BatteryMeterDrawable(mContext, 0));
mBatteryMeterView.mDrawable = mDrawable;
mBatteryMeterView.mAccentColorFilter = mAccentColorFilter;
mBatteryMeterView.mErrorColorFilter = mErrorColorFilter;
doReturn(BATTERY_CRITICAL_LEVEL).when(mDrawable).getCriticalLevel();
} }
@Test @Test
public void testSetBatteryInfo_SetCorrectly() { public void testSetBatteryInfo_setCorrectly() {
mBatteryMeterView.setBatteryInfo(BATTERY_LEVEL); mBatteryMeterView.setBatteryLevel(BATTERY_LEVEL);
verify(mDrawable).setBatteryLevel(BATTERY_LEVEL); assertThat(mDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL);
}
@Test
public void testSetBatteryInfo_levelLow_setErrorColor() {
mBatteryMeterView.setBatteryLevel(BATTERY_LOW_LEVEL);
verify(mDrawable).setBatteryColorFilter(mErrorColorFilter);
}
@Test
public void testSetBatteryInfo_levelNormal_setNormalColor() {
mBatteryMeterView.setBatteryLevel(BATTERY_LEVEL);
verify(mDrawable).setBatteryColorFilter(mAccentColorFilter);
} }
} }

View File

@@ -37,6 +37,8 @@ import com.android.settings.TestConfig;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.LayoutPreference;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
import com.android.settingslib.BatteryInfo; import com.android.settingslib.BatteryInfo;
import org.junit.Before; import org.junit.Before;
@@ -74,7 +76,13 @@ import static org.mockito.Mockito.when;
*/ */
// TODO: Improve this test class so that it starts up the real activity and fragment. // TODO: Improve this test class so that it starts up the real activity and fragment.
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH,
sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
ShadowDynamicIndexableContentMonitor.class
})
public class PowerUsageSummaryTest { public class PowerUsageSummaryTest {
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"}; private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
private static final String TIME_LEFT = "2h30min"; private static final String TIME_LEFT = "2h30min";
@@ -123,8 +131,6 @@ public class PowerUsageSummaryTest {
@Mock @Mock
private LayoutPreference mBatteryLayoutPref; private LayoutPreference mBatteryLayoutPref;
@Mock @Mock
private BatteryMeterView mBatteryMeterView;
@Mock
private TextView mBatteryPercentText; private TextView mBatteryPercentText;
@Mock @Mock
private TextView mSummary1; private TextView mSummary1;
@@ -145,6 +151,7 @@ public class PowerUsageSummaryTest {
private Context mRealContext; private Context mRealContext;
private TestFragment mFragment; private TestFragment mFragment;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
private BatteryMeterView mBatteryMeterView;
@Before @Before
public void setUp() { public void setUp() {
@@ -157,6 +164,8 @@ public class PowerUsageSummaryTest {
mFragment = spy(new TestFragment(mContext)); mFragment = spy(new TestFragment(mContext));
mFragment.initFeatureProvider(); mFragment.initFeatureProvider();
mBatteryMeterView = new BatteryMeterView(mRealContext);
mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
when(mFragment.getActivity()).thenReturn(mSettingsActivity); when(mFragment.getActivity()).thenReturn(mSettingsActivity);
when(mAdditionalBatteryInfoMenu.getItemId()) when(mAdditionalBatteryInfoMenu.getItemId())
@@ -204,7 +213,7 @@ public class PowerUsageSummaryTest {
mFragment.mScreenUsagePref = mScreenUsagePref; mFragment.mScreenUsagePref = mScreenUsagePref;
mFragment.mLastFullChargePref = mLastFullChargePref; mFragment.mLastFullChargePref = mLastFullChargePref;
mBatteryInfo.mBatteryLevel = BATTERY_LEVEL; mBatteryInfo.batteryLevel = BATTERY_LEVEL;
} }
@Test @Test
@@ -411,6 +420,18 @@ public class PowerUsageSummaryTest {
verify(mSummary1).setText(mBatteryInfo.remainingLabel); verify(mSummary1).setText(mBatteryInfo.remainingLabel);
} }
@Test
public void testUpdatePreference_updateBatteryInfo() {
mBatteryInfo.remainingLabel = TIME_LEFT;
mBatteryInfo.batteryLevel = BATTERY_LEVEL;
mBatteryInfo.discharging = true;
mFragment.updateHeaderPreference(mBatteryInfo);
assertThat(mBatteryMeterView.mDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL);
assertThat(mBatteryMeterView.mDrawable.getCharging()).isEqualTo(false);
}
@Test @Test
public void testUpdatePreference_noRemainingTime_showStatusLabel() { public void testUpdatePreference_noRemainingTime_showStatusLabel() {
mBatteryInfo.remainingLabel = null; mBatteryInfo.remainingLabel = null;

View File

@@ -5,9 +5,12 @@ import android.content.res.Resources;
import android.content.res.Resources.NotFoundException; import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme; import android.content.res.Resources.Theme;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.ArrayRes; import android.support.annotation.ArrayRes;
import android.support.annotation.ColorRes;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue; import android.util.TypedValue;
@@ -52,6 +55,14 @@ public class SettingsShadowResources extends ShadowResources {
return directlyOn(realResources, Resources.class).getDimensionPixelSize(id); return directlyOn(realResources, Resources.class).getDimensionPixelSize(id);
} }
@Implementation
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
if (id == R.color.battery_icon_color_error) {
return Color.WHITE;
}
return directlyOn(realResources, Resources.class).getColor(id, theme);
}
@Implementation @Implementation
public Drawable loadDrawable(TypedValue value, int id, Theme theme) public Drawable loadDrawable(TypedValue value, int id, Theme theme)
throws NotFoundException { throws NotFoundException {