diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java index b9194b4709e..fc2bbdf957e 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java @@ -67,7 +67,7 @@ public class BatteryTipLoader extends AsyncLoader> { final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG); final Context context = getContext(); - tips.add(new LowBatteryDetector(policy, batteryInfo).detect()); + tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect()); tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper, batteryInfo.discharging).detect()); tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect()); @@ -87,7 +87,8 @@ public class BatteryTipLoader extends AsyncLoader> { final List tips = new ArrayList<>(); tips.add(new SummaryTip(BatteryTip.StateType.NEW, Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN)); - tips.add(new LowBatteryTip(BatteryTip.StateType.NEW)); + tips.add(new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */, + "Fake data")); return tips; } diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java index 2a6302efbc5..7d52a51dc2f 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java @@ -16,31 +16,52 @@ package com.android.settings.fuelgauge.batterytip.detectors; -import android.text.format.DateUtils; +import android.content.Context; +import android.os.PowerManager; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip; +import java.util.concurrent.TimeUnit; + /** * Detect whether the battery is too low */ public class LowBatteryDetector implements BatteryTipDetector { private BatteryInfo mBatteryInfo; private BatteryTipPolicy mPolicy; + private PowerManager mPowerManager; + private int mWarningLevel; - public LowBatteryDetector(BatteryTipPolicy policy, BatteryInfo batteryInfo) { + public LowBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo) { mPolicy = policy; mBatteryInfo = batteryInfo; + mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mWarningLevel = context.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryWarningLevel); } @Override public BatteryTip detect() { - // Show it if battery life is less than mPolicy.lowBatteryHour - final boolean isShown = mPolicy.lowBatteryEnabled && mBatteryInfo.discharging - && mBatteryInfo.remainingTimeUs < mPolicy.lowBatteryHour * DateUtils.HOUR_IN_MILLIS; + final boolean powerSaveModeOn = mPowerManager.isPowerSaveMode(); + //TODO(jackqdyulei): hook up this 3 hours to server side + final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel + || (mBatteryInfo.discharging + && mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(3)); + + int state = BatteryTip.StateType.INVISIBLE; + if (mPolicy.lowBatteryEnabled) { + if (powerSaveModeOn) { + // Show it is handled if battery saver is on + state = BatteryTip.StateType.HANDLED; + } else if (mBatteryInfo.discharging && lowBattery) { + state = BatteryTip.StateType.NEW; + } + } + return new LowBatteryTip( - isShown ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE); + state, powerSaveModeOn, mBatteryInfo.remainingLabel); } } diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java index f02dd7296b2..2afdc81a54d 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java @@ -74,10 +74,10 @@ public abstract class BatteryTip implements Comparable, Parcelable { TIP_ORDER.append(TipType.APP_RESTRICTION, 0); TIP_ORDER.append(TipType.BATTERY_SAVER, 1); TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 2); - TIP_ORDER.append(TipType.SUMMARY, 3); - TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 4); - TIP_ORDER.append(TipType.REDUCED_BATTERY, 5); - TIP_ORDER.append(TipType.LOW_BATTERY, 6); + TIP_ORDER.append(TipType.LOW_BATTERY, 3); + TIP_ORDER.append(TipType.SUMMARY, 4); + TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 5); + TIP_ORDER.append(TipType.REDUCED_BATTERY, 6); TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 7); } diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java index 86237dd9a98..b48a7dd4680 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java @@ -25,36 +25,32 @@ import com.android.settings.R; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** - * Tip to show current battery life is short + * Tip to show current battery level is low or remaining time is less than a certain period */ -public class LowBatteryTip extends BatteryTip { +public class LowBatteryTip extends EarlyWarningTip { + private CharSequence mSummary; - public LowBatteryTip(@StateType int state) { - super(TipType.LOW_BATTERY, state, false /* showDialog */); + public LowBatteryTip(@StateType int state, boolean powerSaveModeOn, CharSequence summary) { + super(state, powerSaveModeOn); + mType = TipType.LOW_BATTERY; + mSummary = summary; } - private LowBatteryTip(Parcel in) { + public LowBatteryTip(Parcel in) { super(in); - } - - @Override - public CharSequence getTitle(Context context) { - return context.getString(R.string.battery_tip_low_battery_title); + mSummary = in.readCharSequence(); } @Override public CharSequence getSummary(Context context) { - return context.getString(R.string.battery_tip_low_battery_summary); + return mState == StateType.HANDLED ? context.getString( + R.string.battery_tip_early_heads_up_done_summary) : mSummary; } @Override - public int getIconId() { - return R.drawable.ic_perm_device_information_red_24dp; - } - - @Override - public void updateState(BatteryTip tip) { - mState = tip.mState; + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeCharSequence(mSummary); } @Override diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java index b50cc40e711..b0d6a7dce49 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java @@ -48,9 +48,9 @@ public class BatteryTipLoaderTest { BatteryTip.TipType.APP_RESTRICTION, BatteryTip.TipType.BATTERY_SAVER, BatteryTip.TipType.HIGH_DEVICE_USAGE, + BatteryTip.TipType.LOW_BATTERY, BatteryTip.TipType.SUMMARY, - BatteryTip.TipType.SMART_BATTERY_MANAGER, - BatteryTip.TipType.LOW_BATTERY}; + BatteryTip.TipType.SMART_BATTERY_MANAGER}; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private BatteryStatsHelper mBatteryStatsHelper; @Mock diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java index 1f4affae2c1..986363409d8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java @@ -17,12 +17,15 @@ package com.android.settings.fuelgauge.batterytip.detectors; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.spy; -import android.text.format.DateUtils; +import android.content.Context; +import android.os.PowerManager; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; @@ -31,8 +34,12 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowPowerManager; import org.robolectric.util.ReflectionHelpers; +import java.util.concurrent.TimeUnit; + @RunWith(SettingsRobolectricTestRunner.class) public class LowBatteryDetectorTest { @@ -40,36 +47,60 @@ public class LowBatteryDetectorTest { private BatteryInfo mBatteryInfo; private BatteryTipPolicy mPolicy; private LowBatteryDetector mLowBatteryDetector; + private ShadowPowerManager mShadowPowerManager; + private Context mContext; @Before public void setUp() { MockitoAnnotations.initMocks(this); mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application)); + mContext = RuntimeEnvironment.application; + mShadowPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class)); ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true); + mBatteryInfo.discharging = true; - mLowBatteryDetector = new LowBatteryDetector(mPolicy, mBatteryInfo); + mLowBatteryDetector = new LowBatteryDetector(mContext, mPolicy, mBatteryInfo); } @Test public void testDetect_disabledByPolicy_tipInvisible() { ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false); + mShadowPowerManager.setIsPowerSaveMode(true); assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); } @Test - public void testDetect_shortBatteryLife_tipVisible() { - mBatteryInfo.discharging = true; - mBatteryInfo.remainingTimeUs = DateUtils.MINUTE_IN_MILLIS; + public void testDetect_lowBattery_tipNew() { + mBatteryInfo.batteryLevel = 3; + mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1); + assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW); - assertThat(mLowBatteryDetector.detect().isVisible()).isTrue(); + mBatteryInfo.batteryLevel = 50; + mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1); + assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW); } @Test - public void testDetect_longBatteryLife_tipInvisible() { - mBatteryInfo.discharging = true; - mBatteryInfo.remainingTimeUs = DateUtils.DAY_IN_MILLIS; + public void testDetect_batterySaverOn_tipHandled() { + mShadowPowerManager.setIsPowerSaveMode(true); + + assertThat(mLowBatteryDetector.detect().getState()) + .isEqualTo(BatteryTip.StateType.HANDLED); + } + + @Test + public void testDetect_charging_tipInvisible() { + mBatteryInfo.discharging = false; + + assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); + } + + @Test + public void testDetect_noEarlyWarning_tipInvisible() { + mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMicros(1); + mBatteryInfo.batteryLevel = 100; assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java new file mode 100644 index 00000000000..359d2601f81 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 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.batterytip.tips; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.os.Parcel; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +public class LowBatteryTipTest { + + private static final CharSequence SUMMARY = "Only 15 minutes left"; + + @Mock + private MetricsFeatureProvider mMetricsFeatureProvider; + private Context mContext; + private LowBatteryTip mLowBatteryTip; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mLowBatteryTip = new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */, + SUMMARY); + } + + @Test + public void testParcelable() { + Parcel parcel = Parcel.obtain(); + mLowBatteryTip.writeToParcel(parcel, mLowBatteryTip.describeContents()); + parcel.setDataPosition(0); + + final LowBatteryTip parcelTip = new LowBatteryTip(parcel); + + assertThat(parcelTip.isPowerSaveModeOn()).isFalse(); + assertThat(parcelTip.getSummary(mContext)).isEqualTo(SUMMARY); + } + + @Test + public void getSummary_tipHandled_showSummary() { + mLowBatteryTip.mState = BatteryTip.StateType.HANDLED; + + assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo("Some features may be limited"); + } + + @Test + public void getSummary_tipNew_showSummary() { + mLowBatteryTip.mState = BatteryTip.StateType.NEW; + + assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo(SUMMARY); + } + + @Test + public void log_lowBatteryActionWithCorrectState() { + mLowBatteryTip.log(mContext, mMetricsFeatureProvider); + + verify(mMetricsFeatureProvider).action(mContext, + MetricsProto.MetricsEvent.ACTION_LOW_BATTERY_TIP, BatteryTip.StateType.NEW); + } +}