--- /dev/null
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorError">
+ <path
+ android:fillColor="#FF000000"
+ 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>
<string name="battery_tip_summary_title">Battery is in good shape</string>
<!-- Summary for the battery summary tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_summary_summary">Apps are behaving normally</string>
+ <!-- Title for the low battery tip [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_low_battery_title">Low battery capacity</string>
+ <!-- Summary for the low battery tip [CHAR LIMIT=NONE] -->
+ <string name="battery_tip_low_battery_summary">Battery can\'t provide good battery life</string>
<!-- Title for force stop dialog [CHAR LIMIT=30] -->
<string name="dialog_stop_title">Stop app?</string>
package com.android.settings.fuelgauge.batterytip;
import android.content.Context;
+import android.support.annotation.VisibleForTesting;
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.LowBatteryDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settingslib.utils.AsyncLoader;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
private static final boolean USE_FAKE_DATA = false;
private BatteryStatsHelper mBatteryStatsHelper;
+ private BatteryUtils mBatteryUtils;
+ @VisibleForTesting
+ int mVisibleTips;
public BatteryTipLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
super(context);
mBatteryStatsHelper = batteryStatsHelper;
+ mBatteryUtils = BatteryUtils.getInstance(context);
}
@Override
public List<BatteryTip> loadInBackground() {
- List<BatteryTip> tips = new ArrayList<>();
+ if (USE_FAKE_DATA) {
+ return getFakeData();
+ }
+ final List<BatteryTip> tips = new ArrayList<>();
+ final BatteryTipPolicy policy = new BatteryTipPolicy(getContext());
+ final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG);
+ mVisibleTips = 0;
- //TODO(b/70570352): add battery tip detectors
- tips.add(new SummaryTip(BatteryTip.StateType.NEW));
+ addBatteryTipFromDetector(tips, new LowBatteryDetector(policy, batteryInfo));
+ // Add summary detector at last since it need other detectors to update the mVisibleTips
+ addBatteryTipFromDetector(tips, new SummaryDetector(policy, mVisibleTips));
+
+ Collections.sort(tips);
return tips;
}
protected void onDiscardResult(List<BatteryTip> result) {
}
+ private List<BatteryTip> getFakeData() {
+ final List<BatteryTip> tips = new ArrayList<>();
+ tips.add(new SummaryTip(BatteryTip.StateType.NEW));
+ tips.add(new LowBatteryTip(BatteryTip.StateType.NEW));
+
+ return tips;
+ }
+
+ @VisibleForTesting
+ void addBatteryTipFromDetector(final List<BatteryTip> tips,
+ final BatteryTipDetector detector) {
+ final BatteryTip batteryTip = detector.detect();
+ mVisibleTips += batteryTip.isVisible() ? 1 : 0;
+ tips.add(batteryTip);
+ }
+
}
--- /dev/null
+/*
+ * 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.batterytip.detectors;
+
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+
+public interface BatteryTipDetector {
+ /**
+ * Detect and update the status of {@link BatteryTip}
+ *
+ * @return a not null {@link BatteryTip}
+ */
+ BatteryTip detect();
+}
--- /dev/null
+/*
+ * 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.batterytip.detectors;
+
+import android.text.format.DateUtils;
+
+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;
+
+/**
+ * Detect whether the battery is too low
+ */
+public class LowBatteryDetector implements BatteryTipDetector {
+ private BatteryInfo mBatteryInfo;
+ private BatteryTipPolicy mPolicy;
+
+ public LowBatteryDetector(BatteryTipPolicy policy, BatteryInfo batteryInfo) {
+ mPolicy = policy;
+ mBatteryInfo = batteryInfo;
+ }
+
+ @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;
+ return new LowBatteryTip(
+ isShown ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE);
+ }
+}
--- /dev/null
+/*
+ * 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.batterytip.detectors;
+
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
+
+/**
+ * Detector whether to show summary tip. This detector should be executed as the last
+ * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips}
+ */
+public class SummaryDetector implements BatteryTipDetector {
+ private BatteryTipPolicy mPolicy;
+ private int mVisibleTips;
+
+ public SummaryDetector(BatteryTipPolicy policy, int visibleTips) {
+ mPolicy = policy;
+ mVisibleTips = visibleTips;
+ }
+
+ @Override
+ public BatteryTip detect() {
+ // Show it if there is no other tips shown
+ final int state = mPolicy.summaryEnabled && mVisibleTips == 0
+ ? BatteryTip.StateType.NEW
+ : BatteryTip.StateType.INVISIBLE;
+ return new SummaryTip(state);
+ }
+}
* Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the
* pre-defined action(e.g. turn on battery saver)
*/
-public abstract class BatteryTip {
+public abstract class BatteryTip implements Comparable<BatteryTip> {
@Retention(RetentionPolicy.SOURCE)
@IntDef({StateType.NEW,
StateType.HANDLED,
public int getState() {
return mState;
}
+
+ public boolean isVisible() {
+ return mState != StateType.INVISIBLE;
+ }
+
+ @Override
+ public int compareTo(BatteryTip o) {
+ return mType - o.mType;
+ }
}
--- /dev/null
+/*
+ * 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.batterytip.tips;
+
+import android.app.Dialog;
+import android.content.Context;
+
+import com.android.settings.R;
+
+/**
+ * Tip to show current battery life is short
+ */
+public class LowBatteryTip extends BatteryTip {
+
+ public LowBatteryTip(@StateType int state) {
+ mShowDialog = false;
+ mState = state;
+ mType = TipType.LOW_BATTERY;
+ }
+
+ @Override
+ public CharSequence getTitle(Context context) {
+ return context.getString(R.string.battery_tip_low_battery_title);
+ }
+
+ @Override
+ public CharSequence getSummary(Context context) {
+ return context.getString(R.string.battery_tip_low_battery_summary);
+ }
+
+ @Override
+ public int getIconId() {
+ return R.drawable.ic_perm_device_information_red_24dp;
+ }
+
+ @Override
+ public void updateState(BatteryTip tip) {
+ mState = tip.mState;
+ }
+
+ @Override
+ public void action() {
+ // do nothing
+ }
+
+ @Override
+ public Dialog buildDialog() {
+ //TODO(b/70570352): create the dialog for low battery tip and add test
+ return null;
+ }
+}
public SummaryTip(@StateType int state) {
mShowDialog = false;
mState = state;
+ mType = TipType.SUMMARY;
}
@Override
--- /dev/null
+/*
+ * 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.batterytip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.detectors.BatteryTipDetector;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+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 java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryTipLoaderTest {
+ @Mock
+ private BatteryStatsHelper mBatteryStatsHelper;
+ @Mock
+ private BatteryTipDetector mBatteryTipDetector;
+ @Mock
+ private BatteryTip mBatteryTip;
+ private Context mContext;
+ private BatteryTipLoader mBatteryTipLoader;
+ private List<BatteryTip> mBatteryTips;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ doReturn(mBatteryTip).when(mBatteryTipDetector).detect();
+ mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryStatsHelper);
+ mBatteryTips = new ArrayList<>();
+ }
+
+ @Test
+ public void testAddBatteryTipFromDetector_tipVisible_addAndUpdateCount() {
+ doReturn(true).when(mBatteryTip).isVisible();
+ mBatteryTipLoader.mVisibleTips = 0;
+
+ mBatteryTipLoader.addBatteryTipFromDetector(mBatteryTips, mBatteryTipDetector);
+
+ assertThat(mBatteryTips.contains(mBatteryTip)).isTrue();
+ assertThat(mBatteryTipLoader.mVisibleTips).isEqualTo(1);
+ }
+
+ @Test
+ public void testAddBatteryTipFromDetector_tipInvisible_doNotAddCount() {
+ doReturn(false).when(mBatteryTip).isVisible();
+ mBatteryTipLoader.mVisibleTips = 0;
+
+ mBatteryTipLoader.addBatteryTipFromDetector(mBatteryTips, mBatteryTipDetector);
+
+ assertThat(mBatteryTips.contains(mBatteryTip)).isTrue();
+ assertThat(mBatteryTipLoader.mVisibleTips).isEqualTo(0);
+ }
+
+}
--- /dev/null
+/*
+ * 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.batterytip.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.format.DateUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+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 LowBatteryDetectorTest {
+ private Context mContext;
+ @Mock
+ private BatteryInfo mBatteryInfo;
+ private BatteryTipPolicy mPolicy;
+ private LowBatteryDetector mLowBatteryDetector;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mPolicy = spy(new BatteryTipPolicy(mContext));
+ mLowBatteryDetector = new LowBatteryDetector(mPolicy, mBatteryInfo);
+ }
+
+ @Test
+ public void testDetect_disabledByPolicy_tipInvisible() {
+ ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false);
+
+ assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+ }
+
+ @Test
+ public void testDetect_shortBatteryLife_tipVisible() {
+ mBatteryInfo.discharging = true;
+ mBatteryInfo.remainingTimeUs = 1 * DateUtils.MINUTE_IN_MILLIS;
+
+ assertThat(mLowBatteryDetector.detect().isVisible()).isTrue();
+ }
+
+ @Test
+ public void testDetect_longBatteryLife_tipInvisible() {
+ mBatteryInfo.discharging = true;
+ mBatteryInfo.remainingTimeUs = 1 * DateUtils.DAY_IN_MILLIS;
+
+ assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+ }
+}
--- /dev/null
+/*
+ * 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.batterytip.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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 SummaryDetectorTest {
+ private Context mContext;
+ private BatteryTipPolicy mPolicy;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mPolicy = spy(new BatteryTipPolicy(mContext));
+ }
+
+ @Test
+ public void testDetect_disabledByPolicy_tipInvisible() {
+ ReflectionHelpers.setField(mPolicy, "summaryEnabled", false);
+ SummaryDetector detector = new SummaryDetector(mPolicy, 0 /* visibleTips */);
+
+ assertThat(detector.detect().isVisible()).isFalse();
+ }
+
+ @Test
+ public void testDetect_noOtherTips_tipVisible() {
+ SummaryDetector detector = new SummaryDetector(mPolicy, 0 /* visibleTips */);
+
+ assertThat(detector.detect().isVisible()).isTrue();
+ }
+
+ @Test
+ public void testDetect_hasOtherTips_tipInVisible() {
+ SummaryDetector detector = new SummaryDetector(mPolicy, 1 /* visibleTips */);
+
+ assertThat(detector.detect().isVisible()).isFalse();
+ }
+}