diff --git a/res/layout/battery_header.xml b/res/layout/battery_header.xml
new file mode 100644
index 00000000000..a2484c2ad3c
--- /dev/null
+++ b/res/layout/battery_header.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6b835166a48..2bc83c1e960 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -311,4 +311,8 @@
16dp
+
+
+ 66dp
+ 100dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6cb164a0df2..fd476e593bb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4478,6 +4478,12 @@
Usage breakdown since last full charge
+
+ Estimated time left
+
+
+ Estimation may change based on usage
+
%1$s since unplugged
diff --git a/res/xml/account_type_settings.xml b/res/xml/account_type_settings.xml
index 91e90feac0b..6663d7b85ab 100644
--- a/res/xml/account_type_settings.xml
+++ b/res/xml/account_type_settings.xml
@@ -38,6 +38,7 @@
+ android:order="100"
+ android:selectable="false"/>
diff --git a/res/xml/app_storage_settings.xml b/res/xml/app_storage_settings.xml
index 3faf9c8bc64..0254b137ed9 100644
--- a/res/xml/app_storage_settings.xml
+++ b/res/xml/app_storage_settings.xml
@@ -30,6 +30,7 @@
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index b4ac7e2c08d..b6808c000f4 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -20,8 +20,10 @@
android:title="@string/power_usage_summary_title"
settings:keywords="@string/keywords_battery">
-
+
0;
}
@@ -325,7 +351,14 @@ public class PowerUsageSummary extends PowerUsageBase {
protected void refreshStats() {
super.refreshStats();
- updatePreference(mHistPref);
+
+ BatteryInfo.getBatteryInfo(getContext(), new BatteryInfo.Callback() {
+ @Override
+ public void onBatteryInfoLoaded(BatteryInfo info) {
+ updateHeaderPreference(info);
+ }
+ });
+
cacheRemoveAllPrefs(mAppListGroup);
mAppListGroup.setOrderingAsAdded(false);
boolean addedSome = false;
@@ -436,6 +469,27 @@ public class PowerUsageSummary extends PowerUsageBase {
BatteryEntry.startRequestQueue();
}
+ @VisibleForTesting
+ void updateHeaderPreference(BatteryInfo info) {
+ final BatteryMeterView batteryView = (BatteryMeterView) mBatteryLayoutPref
+ .findViewById(R.id.battery_header_icon);
+ final TextView timeText = (TextView) mBatteryLayoutPref.findViewById(R.id.time);
+ final TextView summary1 = (TextView) mBatteryLayoutPref.findViewById(R.id.summary1);
+ final TextView summary2 = (TextView) mBatteryLayoutPref.findViewById(R.id.summary2);
+ final int visible = info.mBatteryLevel != 100 ? View.VISIBLE : View.INVISIBLE;
+
+ if (info.remainingTimeUs != 0) {
+ timeText.setText(Utils.formatElapsedTime(getContext(),
+ info.remainingTimeUs / 1000, false));
+ } else {
+ timeText.setText(info.remainingLabel != null ?
+ info.remainingLabel : info.batteryPercentString);
+ }
+ summary1.setVisibility(visible);
+ summary2.setVisibility(visible);
+ batteryView.setBatteryInfo(info.mBatteryLevel);
+ }
+
@VisibleForTesting
void setUsageSummary(Preference preference, String usedTimePrefix, long usageTimeMs) {
// Only show summary when usage time is longer than one minute
@@ -482,6 +536,11 @@ public class PowerUsageSummary extends PowerUsageBase {
return 0;
}
+ @VisibleForTesting
+ void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
+ mBatteryLayoutPref = layoutPreference;
+ }
+
private static List getFakeStats() {
ArrayList stats = new ArrayList<>();
float use = 5;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java
new file mode 100644
index 00000000000..85b893ae493
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryMeterViewTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.settings.fuelgauge;
+
+import android.content.Context;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
+import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+// TODO: Consider making the shadow class set global using a robolectric.properties file.
+@Config(manifest = TestConfig.MANIFEST_PATH,
+ sdk = TestConfig.SDK_VERSION,
+ shadows = {
+ SettingsShadowResources.class,
+ SettingsShadowTheme.class,
+ ShadowDynamicIndexableContentMonitor.class
+ })
+public class BatteryMeterViewTest {
+ private static final int BATTERY_LEVEL = 100;
+ @Mock
+ private BatteryMeterView.BatteryMeterDrawable mDrawable;
+ private Context mContext;
+ private BatteryMeterView mBatteryMeterView;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mBatteryMeterView = new BatteryMeterView(mContext);
+ mBatteryMeterView.setBatteryDrawable(mDrawable);
+ }
+
+ @Test
+ public void testSetBatteryInfo_SetCorrectly() {
+ mBatteryMeterView.setBatteryInfo(BATTERY_LEVEL);
+
+ verify(mDrawable).setBatteryLevel(BATTERY_LEVEL);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 63cb5e1bdbb..1aacabdfc89 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -23,11 +23,15 @@ import android.text.format.DateUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.settings.R;
import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.BatteryInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +40,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@@ -44,7 +49,6 @@ import static com.android.settings.fuelgauge.PowerUsageBase.MENU_STATS_REFRESH;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADDITIONAL_BATTERY_INFO;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -58,8 +62,11 @@ import static org.mockito.Mockito.when;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PowerUsageSummaryTest {
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
+ private static final String TIME_LEFT = "2h30min";
private static final int UID = 123;
private static final int POWER_MAH = 100;
+ private static final int BATTERY_LEVEL_FULL = 100;
+ private static final int BATTERY_LEVEL_HALF = 50;
private static final double BATTERY_SCREEN_USAGE = 300;
private static final double PRECISION = 0.001;
private static final Intent ADDITIONAL_BATTERY_INFO_INTENT =
@@ -81,6 +88,18 @@ public class PowerUsageSummaryTest {
private BatterySipper mScreenBatterySipper;
@Mock
private PowerGaugePreference mPreference;
+ @Mock
+ private LayoutPreference mBatteryLayoutPref;
+ @Mock
+ private BatteryMeterView mBatteryMeterView;
+ @Mock
+ private TextView mTimeText;
+ @Mock
+ private TextView mSummary1;
+ @Mock
+ private TextView mSummary2;
+ @Mock
+ private BatteryInfo mBatteryInfo;
private TestFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
@@ -111,6 +130,12 @@ public class PowerUsageSummaryTest {
when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
when(mNormalBatterySipper.getUid()).thenReturn(UID);
mNormalBatterySipper.totalPowerMah = POWER_MAH;
+ when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1);
+ when(mBatteryLayoutPref.findViewById(R.id.summary2)).thenReturn(mSummary2);
+ when(mBatteryLayoutPref.findViewById(R.id.time)).thenReturn(mTimeText);
+ when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon))
+ .thenReturn(mBatteryMeterView);
+ mPowerUsageSummary.setBatteryLayoutPreference(mBatteryLayoutPref);
mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
@@ -230,6 +255,28 @@ public class PowerUsageSummaryTest {
verify(mPreference).setSummary(anyString());
}
+ @Test
+ public void testUpdatePreference_BatteryFull_DoNotShowSummary() {
+ mBatteryInfo.mBatteryLevel = BATTERY_LEVEL_FULL;
+ mBatteryInfo.remainingLabel = TIME_LEFT;
+ mPowerUsageSummary.updateHeaderPreference(mBatteryInfo);
+
+ verify(mSummary1).setVisibility(View.INVISIBLE);
+ verify(mSummary2).setVisibility(View.INVISIBLE);
+ verify(mTimeText).setText(mBatteryInfo.remainingLabel);
+ }
+
+ @Test
+ public void testUpdatePreference_BatteryNotFull_ShowSummary() {
+ mBatteryInfo.mBatteryLevel = BATTERY_LEVEL_HALF;
+ mBatteryInfo.remainingLabel = TIME_LEFT;
+ mPowerUsageSummary.updateHeaderPreference(mBatteryInfo);
+
+ verify(mSummary1).setVisibility(View.VISIBLE);
+ verify(mSummary2).setVisibility(View.VISIBLE);
+ verify(mTimeText).setText(mBatteryInfo.remainingLabel);
+ }
+
public static class TestFragment extends PowerUsageSummary {
private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
index 3a1e0b7b41f..cc55a4eb93b 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/SettingsShadowResources.java
@@ -7,6 +7,7 @@ import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.annotation.ArrayRes;
import android.util.AttributeSet;
import android.util.TypedValue;
import org.robolectric.RuntimeEnvironment;
@@ -59,6 +60,16 @@ public class SettingsShadowResources extends ShadowResources {
return super.loadDrawable(value, id, theme);
}
+ @Implementation
+ public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
+ // The Robolectric isn't aware of resources in settingslib, so we need to stub it here
+ if (id == com.android.settings.R.array.batterymeter_bolt_points
+ || id == com.android.settings.R.array.batterymeter_plus_points) {
+ return new int[2];
+ }
+ return directlyOn(realResources, Resources.class).getIntArray(id);
+ }
+
@Implements(Theme.class)
public static class SettingsShadowTheme extends ShadowTheme {