OSDN Git Service

Add early warning tip and detector
authorjackqdyulei <jackqdyulei@google.com>
Wed, 10 Jan 2018 22:29:57 +0000 (14:29 -0800)
committerjackqdyulei <jackqdyulei@google.com>
Tue, 16 Jan 2018 22:10:21 +0000 (14:10 -0800)
When battery cannot make to usual charging time, this tip would
suggest user to turn on battery saver.

This tip has two visible state(NEW, HANDLED) and display different
information.

This cl also adds an action to turn on the battery saver.

Bug: 70570352
Test: RunSettingsRoboTests
Change-Id: I0e96554df12a0d6508c27174e16d8dca7f4e1fce

res/drawable/ic_perm_device_information_green_24dp.xml [new file with mode: 0644]
res/values/strings.xml
src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
src/com/android/settings/fuelgauge/batterytip/actions/BatterySaverAction.java [new file with mode: 0644]
src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java [new file with mode: 0644]
src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java [new file with mode: 0644]

diff --git a/res/drawable/ic_perm_device_information_green_24dp.xml b/res/drawable/ic_perm_device_information_green_24dp.xml
new file mode 100644 (file)
index 0000000..a2b9354
--- /dev/null
@@ -0,0 +1,26 @@
+<!--
+    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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#0F9D58"
+        android:pathData="M13,7h-2v2h2L13,7zM13,11h-2v6h2v-6zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,
+        1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14z"/>
+</vector>
index 46411e6..57eb314 100644 (file)
     <string name="battery_tip_smart_battery_title">Turn on smart battery manager</string>
     <!-- Summary for the smart battery tip [CHAR LIMIT=NONE] -->
     <string name="battery_tip_smart_battery_summary">Turn on to optimize battery usage</string>
+    <!-- Title for the early heads up tip [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_early_heads_up_title">Turn on Low Battery Mode</string>
+    <!-- Summary for the early hedas up tip [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_early_heads_up_summary">Extend your battery life</string>
+    <!-- Title when early heads up is solved [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_early_heads_up_done_title">Low Battery Mode is on</string>
+    <!-- Summary when early heads up is solved [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_early_heads_up_done_summary">Some features are limited</string>
     <!-- Title for the battery high usage tip [CHAR LIMIT=NONE] -->
     <string name="battery_tip_high_usage_title" product="default">Phone used heavily</string>
     <!-- Title for the battery high usage tip [CHAR LIMIT=NONE] -->
index fc6aa57..8c0f54a 100644 (file)
@@ -23,6 +23,7 @@ import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batterytip.detectors.BatteryTipDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.EarlyWarningDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
@@ -64,13 +65,15 @@ public class BatteryTipLoader extends AsyncLoader<List<BatteryTip>> {
         final List<BatteryTip> tips = new ArrayList<>();
         final BatteryTipPolicy policy = new BatteryTipPolicy(getContext());
         final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG);
+        final Context context = getContext();
         mVisibleTips = 0;
 
         addBatteryTipFromDetector(tips, new LowBatteryDetector(policy, batteryInfo));
         addBatteryTipFromDetector(tips,
-                new HighUsageDetector(getContext(), policy, mBatteryStatsHelper));
+                new HighUsageDetector(context, policy, mBatteryStatsHelper));
         addBatteryTipFromDetector(tips,
-                new SmartBatteryDetector(policy, getContext().getContentResolver()));
+                new SmartBatteryDetector(policy, context.getContentResolver()));
+        addBatteryTipFromDetector(tips, new EarlyWarningDetector(policy, context));
         // Add summary detector at last since it need other detectors to update the mVisibleTips
         addBatteryTipFromDetector(tips, new SummaryDetector(policy, mVisibleTips));
 
index d10fa37..5781afd 100644 (file)
@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batterytip;
 import android.app.Fragment;
 
 import com.android.settings.SettingsActivity;
+import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
 import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
 import com.android.settings.fuelgauge.batterytip.actions.SmartBatteryAction;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
@@ -40,6 +41,8 @@ public class BatteryTipUtils {
         switch (batteryTip.getType()) {
             case BatteryTip.TipType.SMART_BATTERY_MANAGER:
                 return new SmartBatteryAction(settingsActivity, fragment);
+            case BatteryTip.TipType.BATTERY_SAVER:
+                return new BatterySaverAction(settingsActivity.getApplicationContext());
             default:
                 return null;
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatterySaverAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatterySaverAction.java
new file mode 100644 (file)
index 0000000..310d3f8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.actions;
+
+import android.content.Context;
+import android.os.PowerManager;
+
+public class BatterySaverAction extends BatteryTipAction {
+    private PowerManager mPowerManager;
+
+    public BatterySaverAction(Context context) {
+        super(context);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+    }
+
+    /**
+     * Handle the action when user clicks positive button
+     */
+    @Override
+    public void handlePositiveAction() {
+        mPowerManager.setPowerSaveMode(true);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java
new file mode 100644 (file)
index 0000000..cb23e94
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.detectors;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.PowerManager;
+
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
+import com.android.settings.overlay.FeatureFactory;
+
+/**
+ * Detector whether to early warning tip.
+ */
+public class EarlyWarningDetector implements BatteryTipDetector {
+    private BatteryTipPolicy mPolicy;
+    private PowerManager mPowerManager;
+    private Context mContext;
+    private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+    public EarlyWarningDetector(BatteryTipPolicy policy, Context context) {
+        mPolicy = policy;
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mContext = context;
+        mPowerUsageFeatureProvider = FeatureFactory.getFactory(
+                context).getPowerUsageFeatureProvider(context);
+    }
+
+    @Override
+    public BatteryTip detect() {
+        final Intent batteryBroadcast = mContext.registerReceiver(null,
+                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        final boolean discharging =
+                batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
+        final boolean powerSaveModeOn = mPowerManager.isPowerSaveMode();
+        final boolean earlyWarning = mPowerUsageFeatureProvider.getEarlyWarningSignal(mContext,
+                EarlyWarningDetector.class.getName());
+
+        final int state =
+                mPolicy.batterySaverTipEnabled && !powerSaveModeOn && discharging && earlyWarning
+                        ? BatteryTip.StateType.NEW
+                        : BatteryTip.StateType.INVISIBLE;
+        return new EarlyWarningTip(state, powerSaveModeOn);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
new file mode 100644 (file)
index 0000000..f8d8fa1
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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 android.content.Context;
+import android.os.Parcel;
+
+import com.android.settings.R;
+
+/**
+ * Tip to show early warning if battery couldn't make to usual charging time
+ */
+public class EarlyWarningTip extends BatteryTip {
+    private boolean mPowerSaveModeOn;
+
+    public EarlyWarningTip(@StateType int state, boolean powerSaveModeOn) {
+        super(TipType.BATTERY_SAVER, state, false /* showDialog */);
+        mPowerSaveModeOn = powerSaveModeOn;
+    }
+
+    public EarlyWarningTip(Parcel in) {
+        super(in);
+        mPowerSaveModeOn = in.readBoolean();
+    }
+
+    @Override
+    public CharSequence getTitle(Context context) {
+        return context.getString(
+                mState == StateType.HANDLED
+                        ? R.string.battery_tip_early_heads_up_done_title
+                        : R.string.battery_tip_early_heads_up_title);
+    }
+
+    @Override
+    public CharSequence getSummary(Context context) {
+        return context.getString(
+                mState == StateType.HANDLED
+                        ? R.string.battery_tip_early_heads_up_done_summary
+                        : R.string.battery_tip_early_heads_up_summary);
+    }
+
+    @Override
+    public int getIconId() {
+        return mState == StateType.HANDLED
+                ? R.drawable.ic_perm_device_information_green_24dp
+                : R.drawable.ic_battery_alert_24dp;
+    }
+
+    @Override
+    public void updateState(BatteryTip tip) {
+        final EarlyWarningTip earlyHeadsUpTip = (EarlyWarningTip) tip;
+        if (mPowerSaveModeOn != earlyHeadsUpTip.mPowerSaveModeOn) {
+            mPowerSaveModeOn = earlyHeadsUpTip.mPowerSaveModeOn;
+            mState = earlyHeadsUpTip.mPowerSaveModeOn ? StateType.HANDLED : StateType.NEW;
+        } else if (mState != StateType.HANDLED) {
+            mState = earlyHeadsUpTip.getState();
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeBoolean(mPowerSaveModeOn);
+    }
+
+    public boolean isPowerSaveModeOn() {
+        return mPowerSaveModeOn;
+    }
+
+    public static final Creator CREATOR = new Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new EarlyWarningTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new EarlyWarningTip[size];
+        }
+    };
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java
new file mode 100644 (file)
index 0000000..ace6da9
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.PowerManager;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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 org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EarlyWarningDetectorTest {
+    private Context mContext;
+    private BatteryTipPolicy mPolicy;
+    private EarlyWarningDetector mEarlyWarningDetector;
+    @Mock
+    private Intent mIntent;
+    @Mock
+    private PowerManager mPowerManager;
+    private FakeFeatureFactory mFakeFeatureFactory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mPolicy = spy(new BatteryTipPolicy(mContext));
+        doReturn(mPowerManager).when(mContext).getSystemService(Context.POWER_SERVICE);
+        doReturn(mIntent).when(mContext).registerReceiver(any(), any());
+        doReturn(0).when(mIntent).getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        doReturn(true).when(mFakeFeatureFactory.powerUsageFeatureProvider).getEarlyWarningSignal(
+                any(), any());
+
+        mEarlyWarningDetector = new EarlyWarningDetector(mPolicy, mContext);
+    }
+
+    @Test
+    public void testDetect_policyDisabled_tipInvisible() {
+        ReflectionHelpers.setField(mPolicy, "batterySaverTipEnabled", false);
+
+        assertThat(mEarlyWarningDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_batterySaverOn_tipInvisible() {
+        doReturn(true).when(mPowerManager).isPowerSaveMode();
+
+        assertThat(mEarlyWarningDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_charging_tipInvisible() {
+        doReturn(1).when(mIntent).getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+
+        assertThat(mEarlyWarningDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_noEarlyWarning_tipInvisible() {
+        doReturn(false).when(mFakeFeatureFactory.powerUsageFeatureProvider).getEarlyWarningSignal(
+                any(), any());
+
+        assertThat(mEarlyWarningDetector.detect().isVisible()).isFalse();
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
new file mode 100644 (file)
index 0000000..66d5f81
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 android.content.Context;
+import android.os.Parcel;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EarlyWarningTipTest {
+    private Context mContext;
+    private EarlyWarningTip mEarlyWarningTip;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mEarlyWarningTip = new EarlyWarningTip(BatteryTip.StateType.NEW,
+                false /* powerSaveModeOn */);
+    }
+
+    @Test
+    public void testParcelable() {
+        Parcel parcel = Parcel.obtain();
+        mEarlyWarningTip.writeToParcel(parcel, mEarlyWarningTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final EarlyWarningTip parcelTip = new EarlyWarningTip(parcel);
+
+        assertThat(parcelTip.isPowerSaveModeOn()).isFalse();
+    }
+
+    @Test
+    public void testInfo_stateNew_displayPowerModeInfo() {
+        final EarlyWarningTip tip = new EarlyWarningTip(BatteryTip.StateType.NEW,
+                false /* powerModeOn */);
+
+        assertThat(tip.getTitle(mContext)).isEqualTo("Turn on Low Battery Mode");
+        assertThat(tip.getSummary(mContext)).isEqualTo("Extend your battery life");
+        assertThat(tip.getIconId()).isEqualTo(R.drawable.ic_battery_alert_24dp);
+    }
+
+    @Test
+    public void testInfo_stateHandled_displayPowerModeHandledInfo() {
+        final EarlyWarningTip tip = new EarlyWarningTip(BatteryTip.StateType.HANDLED,
+                false /* powerModeOn */);
+
+        assertThat(tip.getTitle(mContext)).isEqualTo("Low Battery Mode is on");
+        assertThat(tip.getSummary(mContext)).isEqualTo("Some features are limited");
+        assertThat(tip.getIconId()).isEqualTo(R.drawable.ic_perm_device_information_green_24dp);
+    }
+
+    @Test
+    public void testUpdate_powerModeTurnedOn_typeBecomeHandled() {
+        final EarlyWarningTip nextTip = new EarlyWarningTip(BatteryTip.StateType.INVISIBLE,
+                true /* powerModeOn */);
+
+        mEarlyWarningTip.updateState(nextTip);
+
+        assertThat(mEarlyWarningTip.getState()).isEqualTo(BatteryTip.StateType.HANDLED);
+    }
+}