diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4b2c67344dc..1c58a90ea95 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4842,6 +4842,28 @@
Your tablet was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your tablet was used for about %1$s since last full charge.\n\n Total usage:
Your device was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your device was used for about %1$s since last full charge.\n\n Total usage:
+
+
+ - Restrict %1$d app
+ - Restrict %1$d apps
+
+
+
+ - %1$d recently restricted
+ - %1$d apps recently restricted
+
+
+
+ - %1$s has high battery usage
+ - %2$d apps have high battery usage
+
+
+ App changes are in progress
+
+
+ To save battery, you can stop this app from running in the background when it’s not being used.
+
+ Restrict
Smart battery manager
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index b51474defaa..6d9aaabb810 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -34,6 +34,9 @@ import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController.
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+
+import java.util.List;
/**
* Dialog Fragment to show action dialog for each anomaly
@@ -84,6 +87,23 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
.setView(view)
.setPositiveButton(android.R.string.ok, null)
.create();
+ case BatteryTip.TipType.APP_RESTRICTION:
+ final RestrictAppTip restrictAppTip = (RestrictAppTip) mBatteryTip;
+ final RecyclerView restrictionView = (RecyclerView) LayoutInflater.from(
+ context).inflate(R.layout.recycler_view, null);
+ final List restrictedAppList = restrictAppTip.getRestrictAppList();
+ final int num = restrictedAppList.size();
+ restrictionView.setLayoutManager(new LinearLayoutManager(context));
+ restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList));
+
+ return new AlertDialog.Builder(context)
+ .setTitle(context.getResources().getQuantityString(
+ R.plurals.battery_tip_restrict_title, num, num))
+ .setMessage(getString(R.string.battery_tip_restrict_app_dialog_message))
+ .setView(restrictionView)
+ .setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this)
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
default:
throw new IllegalArgumentException("unknown type " + mBatteryTip.getType());
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index ced34616879..a61584168bc 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -26,6 +26,7 @@ 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;
+import com.android.settings.fuelgauge.batterytip.detectors.RestrictAppDetector;
import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
@@ -70,6 +71,7 @@ public class BatteryTipLoader extends AsyncLoader> {
tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
tips.add(new EarlyWarningDetector(policy, context).detect());
tips.add(new SummaryDetector(policy).detect());
+ tips.add(new RestrictAppDetector(policy).detect());
Collections.sort(tips);
return tips;
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
index 60aa6c8832a..6c129d8a9be 100644
--- a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
@@ -77,7 +77,9 @@ public class HighUsageAdapter extends RecyclerView.Adapter highUsageApps = new ArrayList<>();
+ return new RestrictAppTip(
+ highUsageApps.isEmpty() ? BatteryTip.StateType.INVISIBLE : BatteryTip.StateType.NEW,
+ highUsageApps);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
new file mode 100644
index 00000000000..054b6e19421
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
@@ -0,0 +1,99 @@
+/*
+ * 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.content.res.Resources;
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+
+import java.util.List;
+
+/**
+ * Tip to suggest user to restrict some bad apps
+ */
+public class RestrictAppTip extends BatteryTip {
+ private List mRestrictAppList;
+
+ public RestrictAppTip(@StateType int state, List highUsageApps) {
+ super(TipType.APP_RESTRICTION, state, true /* showDialog */);
+ mRestrictAppList = highUsageApps;
+ }
+
+ @VisibleForTesting
+ RestrictAppTip(Parcel in) {
+ super(in);
+ mRestrictAppList = in.createTypedArrayList(AppInfo.CREATOR);
+ }
+
+ @Override
+ public CharSequence getTitle(Context context) {
+ final int num = mRestrictAppList.size();
+ return context.getResources().getQuantityString(
+ mState == StateType.HANDLED
+ ? R.plurals.battery_tip_restrict_handled_title
+ : R.plurals.battery_tip_restrict_title,
+ num, num);
+ }
+
+ @Override
+ public CharSequence getSummary(Context context) {
+ final int num = mRestrictAppList.size();
+ final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context,
+ mRestrictAppList.get(0).packageName) : "";
+ return mState == StateType.HANDLED
+ ? context.getString(R.string.battery_tip_restrict_handled_summary)
+ : context.getResources().getQuantityString(R.plurals.battery_tip_restrict_summary,
+ num, appLabel, num);
+ }
+
+ @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) {
+ mState = tip.mState;
+ }
+
+ public List getRestrictAppList() {
+ return mRestrictAppList;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeTypedList(mRestrictAppList);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ public BatteryTip createFromParcel(Parcel in) {
+ return new RestrictAppTip(in);
+ }
+
+ public BatteryTip[] newArray(int size) {
+ return new RestrictAppTip[size];
+ }
+ };
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index ddee31461c3..a5815016275 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -27,7 +27,9 @@ import android.text.format.DateUtils;
import com.android.settings.R;
import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowRuntimePermissionPresenter;
@@ -55,6 +57,7 @@ public class BatteryTipDialogFragmentTest {
private BatteryTipDialogFragment mDialogFragment;
private Context mContext;
private HighUsageTip mHighUsageTip;
+ private RestrictAppTip mRestrictedAppTip;
@Before
public void setUp() {
@@ -67,6 +70,7 @@ public class BatteryTipDialogFragmentTest {
highUsageTips.add(new AppInfo.Builder().setScreenOnTimeMs(SCREEN_TIME_MS).setPackageName(
PACKAGE_NAME).build());
mHighUsageTip = new HighUsageTip(SCREEN_TIME_MS, highUsageTips);
+ mRestrictedAppTip = new RestrictAppTip(BatteryTip.StateType.NEW, highUsageTips);
}
@Test
@@ -82,5 +86,19 @@ public class BatteryTipDialogFragmentTest {
mContext.getString(R.string.battery_tip_dialog_message, "1h"));
}
+ @Test
+ public void testOnCreateDialog_restrictAppTip_fireRestrictAppDialog() {
+ mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictedAppTip);
+
+ FragmentTestUtil.startFragment(mDialogFragment);
+
+ final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+ ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+ assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 1 app");
+ assertThat(shadowDialog.getMessage()).isEqualTo(
+ mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+ }
+
}
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 83b32258009..09e67edbdbc 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -51,6 +51,7 @@ import java.util.List;
public class BatteryTipLoaderTest {
private static final int[] TIP_ORDER = {
BatteryTip.TipType.SMART_BATTERY_MANAGER,
+ BatteryTip.TipType.APP_RESTRICTION,
BatteryTip.TipType.HIGH_DEVICE_USAGE,
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.LOW_BATTERY,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
new file mode 100644
index 00000000000..e1dea17c1f6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+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 RestrictAppTipTest {
+ private static final String PACKAGE_NAME = "com.android.app";
+ private static final String DISPLAY_NAME = "app";
+
+ private Context mContext;
+ private RestrictAppTip mNewBatteryTip;
+ private RestrictAppTip mHandledBatteryTip;
+ private List mUsageAppList;
+ @Mock
+ private ApplicationInfo mApplicationInfo;
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
+ PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_ANY_USER);
+ doReturn(DISPLAY_NAME).when(mApplicationInfo).loadLabel(mPackageManager);
+
+ mUsageAppList = new ArrayList<>();
+ mUsageAppList.add(new AppInfo.Builder()
+ .setPackageName(PACKAGE_NAME)
+ .build());
+ mNewBatteryTip = new RestrictAppTip(BatteryTip.StateType.NEW, mUsageAppList);
+ mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList);
+ }
+
+ @Test
+ public void testParcelable() {
+ Parcel parcel = Parcel.obtain();
+ mNewBatteryTip.writeToParcel(parcel, mNewBatteryTip.describeContents());
+ parcel.setDataPosition(0);
+
+ final RestrictAppTip parcelTip = new RestrictAppTip(parcel);
+
+ assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.APP_RESTRICTION);
+ assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+ final AppInfo app = parcelTip.getRestrictAppList().get(0);
+ assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
+ }
+
+ @Test
+ public void testGetTitle_stateNew_showRestrictTitle() {
+ assertThat(mNewBatteryTip.getTitle(mContext)).isEqualTo("Restrict 1 app");
+ }
+
+ @Test
+ public void testGetTitle_stateHandled_showHandledTitle() {
+ assertThat(mHandledBatteryTip.getTitle(mContext)).isEqualTo("1 recently restricted");
+ }
+
+ @Test
+ public void testGetSummary_stateNew_showRestrictSummary() {
+ assertThat(mNewBatteryTip.getSummary(mContext)).isEqualTo(
+ "app has high battery usage");
+ }
+
+ @Test
+ public void testGetSummary_stateHandled_showHandledSummary() {
+ assertThat(mHandledBatteryTip.getSummary(mContext)).isEqualTo(
+ "App changes are in progress");
+ }
+}