From ab0cde6bad5005e5c7fba3e9e17ca6b6408a09ff Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 12 Jan 2018 13:52:20 -0800 Subject: [PATCH 1/2] Add app restrict tip and detector 1. Add RestrictAppTip with two state(new and handled) 2. Add RestrictAppDetector, and future cl will hook up it with anomaly database 3. Add related dialog in BatteryTipDialogFragment Bug: 72385333 Test: RunSettingsRoboTests Change-Id: Ic10efc6387150e62b6c6ad8d4c0d16ff75564fac --- res/values/strings.xml | 22 ++++ .../batterytip/BatteryTipDialogFragment.java | 20 ++++ .../batterytip/BatteryTipLoader.java | 2 + .../batterytip/HighUsageAdapter.java | 4 +- .../detectors/RestrictAppDetector.java | 46 ++++++++ .../batterytip/tips/RestrictAppTip.java | 99 ++++++++++++++++ .../BatteryTipDialogFragmentTest.java | 18 +++ .../batterytip/BatteryTipLoaderTest.java | 1 + .../batterytip/tips/RestrictAppTipTest.java | 111 ++++++++++++++++++ 9 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java create mode 100644 src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java 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"); + } +} From 99a2de41ef8ff7eb4ea47413d5ad8c930c00d881 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Thu, 25 Jan 2018 18:00:01 -0800 Subject: [PATCH 2/2] Add restrict and unrestrict dialog Add a fake unrestrict tip so we could reuse the BatteryTipDialogFragment to build the unrestrict dialog. After this cl, restrict dialog has two types: 1. dialog to only restrict one app 2. dialog to restrict more than one app Will add dialog to restrict more than 5 apps when strings are finalized. Bug: 72385333 Bug: 72227981 Test: RunSettingsRoboTests Change-Id: Ib0328f0386efad525b331fd713dd15d060a1a649 --- res/values/strings.xml | 13 +++ .../fuelgauge/AdvancedPowerUsageDetail.java | 8 +- ...ackgroundActivityPreferenceController.java | 79 ++++------------- .../batterytip/BatteryTipDialogFragment.java | 37 +++++--- .../fuelgauge/batterytip/BatteryTipUtils.java | 11 ++- .../batterytip/actions/RestrictAppAction.java | 57 +++++++++++++ .../actions/UnrestrictAppAction.java | 48 +++++++++++ .../fuelgauge/batterytip/tips/BatteryTip.java | 4 +- .../batterytip/tips/RestrictAppTip.java | 11 ++- .../batterytip/tips/UnrestrictAppTip.java | 84 +++++++++++++++++++ ...roundActivityPreferenceControllerTest.java | 20 +---- .../settings/fuelgauge/BatteryUtilsTest.java | 24 ++++++ .../BatteryTipDialogFragmentTest.java | 64 ++++++++++++-- .../actions/RestrictAppActionTest.java | 83 ++++++++++++++++++ .../batterytip/tips/UnrestrictAppTipTest.java | 61 ++++++++++++++ .../testutils/shadow/ShadowUtils.java | 19 +++++ 16 files changed, 517 insertions(+), 106 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java create mode 100644 src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java create mode 100644 src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 1c58a90ea95..3d917c3c6f6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4860,10 +4860,23 @@ App changes are in progress + + + Restrict app? + Restrict %1$d apps? + To save battery, you can stop this app from running in the background when it’s not being used. Restrict + + Remove restriction for %1$s? + + This app will be able to use battery in the background. This may cause your battery to be used up faster. + + Remove + + Not now Smart battery manager diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index de027a3a199..e073456db82 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -46,6 +46,8 @@ import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.fuelgauge.anomaly.AnomalyUtils; +import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment; @@ -69,7 +71,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements ButtonActionDialogFragment.AppButtonsDialogListener, AnomalyDialogFragment.AnomalyDialogListener, LoaderManager.LoaderCallbacks>, - BackgroundActivityPreferenceController.WarningConfirmationListener { + BatteryTipPreferenceController.BatteryTipListener { public static final String TAG = "AdvancedPowerUsageDetail"; public static final String EXTRA_UID = "extra_uid"; @@ -373,8 +375,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements } @Override - public void onLimitBackgroundActivity() { - mBackgroundActivityPreferenceController.setRestricted( + public void onBatteryTipHandled(BatteryTip batteryTip) { + mBackgroundActivityPreferenceController.updateSummary( findPreference(mBackgroundActivityPreferenceController.getPreferenceKey())); } } diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java index 01e41825074..21bd4b73181 100644 --- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java @@ -14,27 +14,22 @@ package com.android.settings.fuelgauge; -import android.app.AlertDialog; import android.app.AppOpsManager; -import android.app.Dialog; import android.app.Fragment; import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.content.DialogInterface; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; import android.os.UserManager; import android.support.annotation.VisibleForTesting; -import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; -import android.util.Log; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; +import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; +import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.wrapper.DevicePolicyManagerWrapper; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; @@ -99,15 +94,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo return mTargetPackage != null; } - /** - * Called from the warning dialog, if the user decides to go ahead and disable background - * activity for this package - */ - public void setRestricted(Preference preference) { - mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED); - updateSummary(preference); - } - @Override public String getPreferenceKey() { return KEY_BACKGROUND_ACTIVITY; @@ -119,20 +105,13 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); final boolean restricted = mode == AppOpsManager.MODE_IGNORED; - if (!restricted) { - showDialog(); - return false; - } - mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED); - updateSummary(preference); - return true; + showDialog(restricted); } return false; } - @VisibleForTesting - void updateSummary(Preference preference) { + public void updateSummary(Preference preference) { if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) { preference.setSummary(R.string.background_activity_summary_whitelisted); return; @@ -150,42 +129,16 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo } @VisibleForTesting - void showDialog() { - final WarningDialogFragment dialogFragment = new WarningDialogFragment(); + void showDialog(boolean restricted) { + final AppInfo appInfo = new AppInfo.Builder() + .setPackageName(mTargetPackage) + .build(); + BatteryTip tip = restricted + ? new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo) + : new RestrictAppTip(BatteryTip.StateType.NEW, appInfo); + + final BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(tip); dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */); dialogFragment.show(mFragment.getFragmentManager(), TAG); } - - interface WarningConfirmationListener { - void onLimitBackgroundActivity(); - } - - /** - * Warning dialog to show to the user as turning off background activity can lead to - * apps misbehaving as their background task scheduling guarantees will no longer be honored. - */ - public static class WarningDialogFragment extends InstrumentedDialogFragment { - @Override - public int getMetricsCategory() { - // TODO (b/65494831): add metric id - return 0; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final WarningConfirmationListener listener = - (WarningConfirmationListener) getTargetFragment(); - return new AlertDialog.Builder(getContext()) - .setTitle(R.string.background_activity_warning_dialog_title) - .setMessage(R.string.background_activity_warning_dialog_text) - .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - listener.onLimitBackgroundActivity(); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - } - } } diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java index 6d9aaabb810..66ce3caad01 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java @@ -35,6 +35,7 @@ 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 com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import java.util.List; @@ -89,20 +90,36 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme .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)); + + final AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setTitle(context.getResources().getQuantityString( + R.plurals.battery_tip_restrict_app_dialog_title, num, num)) + .setMessage(getString(R.string.battery_tip_restrict_app_dialog_message)) + .setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this) + .setNegativeButton(android.R.string.cancel, null); + + // TODO(b/72385333): consider building dialog with 5+ apps when strings are done + if (num > 1) { + final RecyclerView restrictionView = (RecyclerView) LayoutInflater.from( + context).inflate(R.layout.recycler_view, null); + restrictionView.setLayoutManager(new LinearLayoutManager(context)); + restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList)); + builder.setView(restrictionView); + } + + return builder.create(); + case BatteryTip.TipType.REMOVE_APP_RESTRICTION: + final UnrestrictAppTip unrestrictAppTip = (UnrestrictAppTip) mBatteryTip; + final CharSequence name = Utils.getApplicationLabel(context, + unrestrictAppTip.getPackageName()); 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) + .setTitle(getString(R.string.battery_tip_unrestrict_app_dialog_title, name)) + .setMessage(R.string.battery_tip_unrestrict_app_dialog_message) + .setPositiveButton(R.string.battery_tip_unrestrict_app_dialog_ok, this) + .setNegativeButton(R.string.battery_tip_unrestrict_app_dialog_cancel, null) .create(); default: throw new IllegalArgumentException("unknown type " + mBatteryTip.getType()); diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java index 5781afd61f2..5eec3229f80 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java @@ -17,12 +17,17 @@ package com.android.settings.fuelgauge.batterytip; import android.app.Fragment; +import android.content.Context; 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.RestrictAppAction; import com.android.settings.fuelgauge.batterytip.actions.SmartBatteryAction; +import com.android.settings.fuelgauge.batterytip.actions.UnrestrictAppAction; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; +import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; +import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; /** * Utility class for {@link BatteryTip} @@ -42,7 +47,11 @@ public class BatteryTipUtils { case BatteryTip.TipType.SMART_BATTERY_MANAGER: return new SmartBatteryAction(settingsActivity, fragment); case BatteryTip.TipType.BATTERY_SAVER: - return new BatterySaverAction(settingsActivity.getApplicationContext()); + return new BatterySaverAction(settingsActivity); + case BatteryTip.TipType.APP_RESTRICTION: + return new RestrictAppAction(settingsActivity, (RestrictAppTip) batteryTip); + case BatteryTip.TipType.REMOVE_APP_RESTRICTION: + return new UnrestrictAppAction(settingsActivity, (UnrestrictAppTip) batteryTip); default: return null; } diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java new file mode 100644 index 00000000000..9c49822bb18 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java @@ -0,0 +1,57 @@ +/* + * 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.app.AppOpsManager; +import android.content.Context; +import android.support.annotation.VisibleForTesting; + +import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; + +import java.util.List; + +/** + * Action to restrict the apps, then app is not allowed to run in the background. + */ +public class RestrictAppAction extends BatteryTipAction { + private RestrictAppTip mRestrictAppTip; + @VisibleForTesting + BatteryUtils mBatteryUtils; + + public RestrictAppAction(Context context, RestrictAppTip tip) { + super(context); + mRestrictAppTip = tip; + mBatteryUtils = BatteryUtils.getInstance(context); + } + + /** + * Handle the action when user clicks positive button + */ + @Override + public void handlePositiveAction() { + final List appInfos = mRestrictAppTip.getRestrictAppList(); + + for (int i = 0, size = appInfos.size(); i < size; i++) { + final String packageName = appInfos.get(i).packageName; + // Force app standby, then app can't run in the background + mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName, + AppOpsManager.MODE_IGNORED); + } + } +} diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java new file mode 100644 index 00000000000..16812f69afa --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java @@ -0,0 +1,48 @@ +/* + * 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.app.AppOpsManager; +import android.content.Context; + +import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; + +/** + * Action to clear the restriction to the app + */ +public class UnrestrictAppAction extends BatteryTipAction { + private UnrestrictAppTip mUnRestrictAppTip; + private BatteryUtils mBatteryUtils; + + public UnrestrictAppAction(Context context, UnrestrictAppTip tip) { + super(context); + mUnRestrictAppTip = tip; + mBatteryUtils = BatteryUtils.getInstance(context); + } + + /** + * Handle the action when user clicks positive button + */ + @Override + public void handlePositiveAction() { + final String packageName = mUnRestrictAppTip.getPackageName(); + // Clear force app standby, then app can run in the background + mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName, + AppOpsManager.MODE_ALLOWED); + } +} diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java index 09ebc4b5b14..59cd5ee4226 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java @@ -50,7 +50,8 @@ public abstract class BatteryTip implements Comparable, Parcelable { TipType.SMART_BATTERY_MANAGER, TipType.APP_RESTRICTION, TipType.REDUCED_BATTERY, - TipType.LOW_BATTERY}) + TipType.LOW_BATTERY, + TipType.REMOVE_APP_RESTRICTION}) public @interface TipType { int SMART_BATTERY_MANAGER = 0; int APP_RESTRICTION = 1; @@ -59,6 +60,7 @@ public abstract class BatteryTip implements Comparable, Parcelable { int REDUCED_BATTERY = 4; int LOW_BATTERY = 5; int SUMMARY = 6; + int REMOVE_APP_RESTRICTION = 7; } private static final String KEY_PREFIX = "key_battery_tip"; diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java index 054b6e19421..1d84d7fb94b 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java @@ -25,6 +25,7 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.fuelgauge.batterytip.AppInfo; +import java.util.ArrayList; import java.util.List; /** @@ -33,9 +34,15 @@ import java.util.List; public class RestrictAppTip extends BatteryTip { private List mRestrictAppList; - public RestrictAppTip(@StateType int state, List highUsageApps) { + public RestrictAppTip(@StateType int state, List restrictApps) { super(TipType.APP_RESTRICTION, state, true /* showDialog */); - mRestrictAppList = highUsageApps; + mRestrictAppList = restrictApps; + } + + public RestrictAppTip(@StateType int state, AppInfo appInfo) { + super(TipType.APP_RESTRICTION, state, true /* showDialog */); + mRestrictAppList = new ArrayList<>(); + mRestrictAppList.add(appInfo); } @VisibleForTesting diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java new file mode 100644 index 00000000000..ec67f6a47b1 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java @@ -0,0 +1,84 @@ +/* + * 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.internal.annotations.VisibleForTesting; +import com.android.settings.fuelgauge.batterytip.AppInfo; + +/** + * Tip to suggest user to remove app restriction. This is the empty tip and it is only used in + * {@link com.android.settings.fuelgauge.AdvancedPowerUsageDetail} to create dialog. + */ +public class UnrestrictAppTip extends BatteryTip { + private AppInfo mAppInfo; + + public UnrestrictAppTip(@StateType int state, AppInfo appInfo) { + super(TipType.REMOVE_APP_RESTRICTION, state, true /* showDialog */); + mAppInfo = appInfo; + } + + @VisibleForTesting + UnrestrictAppTip(Parcel in) { + super(in); + mAppInfo = in.readParcelable(getClass().getClassLoader()); + } + + @Override + public CharSequence getTitle(Context context) { + // Don't need title since this is an empty tip + return null; + } + + @Override + public CharSequence getSummary(Context context) { + // Don't need summary since this is an empty tip + return null; + } + + @Override + public int getIconId() { + return 0; + } + + public String getPackageName() { + return mAppInfo.packageName; + } + + @Override + public void updateState(BatteryTip tip) { + mState = tip.mState; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mAppInfo, flags); + } + + public static final Creator CREATOR = new Creator() { + public BatteryTip createFromParcel(Parcel in) { + return new UnrestrictAppTip(in); + } + + public BatteryTip[] newArray(int size) { + return new UnrestrictAppTip[size]; + } + }; +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java index 30fdccb23ec..0cfe135d9c7 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java @@ -142,18 +142,17 @@ public class BackgroundActivityPreferenceControllerTest { mController.handlePreferenceTreeClick(mPreference); - verify(mController).showDialog(); + verify(mController).showDialog(false /* restrict */); } @Test - public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() { + public void testHandlePreferenceTreeClick_unRestrictApp_showDialog() { doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(), anyInt(), anyString()); mController.handlePreferenceTreeClick(mPreference); - verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE, - AppOpsManager.MODE_ALLOWED); + verify(mController).showDialog(true /* restrict */); } @Test @@ -211,17 +210,4 @@ public class BackgroundActivityPreferenceControllerTest { public void testIsAvailable_ReturnTrue() { assertThat(mController.isAvailable()).isTrue(); } - - @Test - public void testWarningDialog() { - BackgroundActivityPreferenceController.WarningDialogFragment dialogFragment = - new BackgroundActivityPreferenceController.WarningDialogFragment(); - dialogFragment.setTargetFragment(mFragment, 0); - FragmentTestUtil.startFragment(dialogFragment); - final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog(); - ShadowAlertDialog shadowDialog = shadowOf(dialog); - final Button okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - shadowDialog.clickOn(okButton.getId()); - verify(mFragment).onLimitBackgroundActivity(); - } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index fe907510117..6bc6ee71664 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -523,4 +524,27 @@ public class BatteryUtilsTest { public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() { assertThat(mBatteryUtils.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse(); } + + @Test + public void testSetForceAppStandby_forcePreOApp_forceTwoRestrictions() { + mBatteryUtils.setForceAppStandby(UID, LOW_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); + + // Restrict both OP_RUN_IN_BACKGROUND and OP_RUN_ANY_IN_BACKGROUND + verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, LOW_SDK_PACKAGE, + AppOpsManager.MODE_IGNORED); + verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, LOW_SDK_PACKAGE, + AppOpsManager.MODE_IGNORED); + } + + @Test + public void testSetForceAppStandby_forceOApp_forceOneRestriction() { + mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); + + // Don't restrict OP_RUN_IN_BACKGROUND because it is already been restricted for O app + verify(mAppOpsManager, never()).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, + HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); + // Restrict OP_RUN_ANY_IN_BACKGROUND + verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, + HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); + } } 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 a5815016275..ec7238449dd 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java @@ -30,9 +30,10 @@ 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.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.testutils.shadow.ShadowRuntimePermissionPresenter; +import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; import org.junit.Test; @@ -49,15 +50,18 @@ import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, - shadows = ShadowRuntimePermissionPresenter.class) + shadows = ShadowUtils.class) public class BatteryTipDialogFragmentTest { private static final String PACKAGE_NAME = "com.android.app"; + private static final String DISPLAY_NAME = "app"; private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS; private BatteryTipDialogFragment mDialogFragment; private Context mContext; private HighUsageTip mHighUsageTip; - private RestrictAppTip mRestrictedAppTip; + private RestrictAppTip mRestrictedOneAppTip; + private RestrictAppTip mRestrictAppsTip; + private UnrestrictAppTip mUnrestrictAppTip; @Before public void setUp() { @@ -67,10 +71,22 @@ public class BatteryTipDialogFragmentTest { FakeFeatureFactory.setupForTest(); List highUsageTips = new ArrayList<>(); - highUsageTips.add(new AppInfo.Builder().setScreenOnTimeMs(SCREEN_TIME_MS).setPackageName( - PACKAGE_NAME).build()); + final AppInfo appInfo = new AppInfo.Builder() + .setScreenOnTimeMs(SCREEN_TIME_MS) + .setPackageName(PACKAGE_NAME) + .build(); + highUsageTips.add(appInfo); mHighUsageTip = new HighUsageTip(SCREEN_TIME_MS, highUsageTips); - mRestrictedAppTip = new RestrictAppTip(BatteryTip.StateType.NEW, highUsageTips); + + final List restrictApps = new ArrayList<>(); + restrictApps.add(appInfo); + mRestrictedOneAppTip = new RestrictAppTip(BatteryTip.StateType.NEW, + new ArrayList<>(restrictApps)); + restrictApps.add(appInfo); + mRestrictAppsTip = new RestrictAppTip(BatteryTip.StateType.NEW, + new ArrayList<>(restrictApps)); + + mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo); } @Test @@ -87,18 +103,48 @@ public class BatteryTipDialogFragmentTest { } @Test - public void testOnCreateDialog_restrictAppTip_fireRestrictAppDialog() { - mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictedAppTip); + public void testOnCreateDialog_restrictOneAppTip_fireRestrictOneAppDialog() { + mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictedOneAppTip); FragmentTestUtil.startFragment(mDialogFragment); final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog(); ShadowAlertDialog shadowDialog = shadowOf(dialog); - assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 1 app"); + assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?"); assertThat(shadowDialog.getMessage()).isEqualTo( mContext.getString(R.string.battery_tip_restrict_app_dialog_message)); } + @Test + public void testOnCreateDialog_restrictAppsTip_fireRestrictAppsDialog() { + mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictAppsTip); + + FragmentTestUtil.startFragment(mDialogFragment); + + final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog(); + ShadowAlertDialog shadowDialog = shadowOf(dialog); + + assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 2 apps?"); + assertThat(shadowDialog.getMessage()).isEqualTo( + mContext.getString(R.string.battery_tip_restrict_app_dialog_message)); + assertThat(shadowDialog.getView()).isNotNull(); + } + + @Test + public void testOnCreateDialog_unRestrictAppTip_fireUnRestrictDialog() { + mDialogFragment = BatteryTipDialogFragment.newInstance(mUnrestrictAppTip); + ShadowUtils.setApplicationLabel(PACKAGE_NAME, DISPLAY_NAME); + + FragmentTestUtil.startFragment(mDialogFragment); + + final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog(); + ShadowAlertDialog shadowDialog = shadowOf(dialog); + + assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction for app?"); + assertThat(shadowDialog.getMessage()).isEqualTo( + mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message)); + } + } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java new file mode 100644 index 00000000000..47785d555b3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java @@ -0,0 +1,83 @@ +/* + * 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 static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +import android.app.AppOpsManager; +import android.content.Context; + +import com.android.settings.TestConfig; +import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; +import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; +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 RestrictAppActionTest { + private static final String PACKAGE_NAME_1 = "com.android.app1"; + private static final String PACKAGE_NAME_2 = "com.android.app2"; + + @Mock + private BatteryUtils mBatteryUtils; + private Context mContext; + private RestrictAppAction mRestrictAppAction; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + final List mAppInfos = new ArrayList<>(); + mAppInfos.add(new AppInfo.Builder() + .setPackageName(PACKAGE_NAME_1) + .build()); + mAppInfos.add(new AppInfo.Builder() + .setPackageName(PACKAGE_NAME_2) + .build()); + + mRestrictAppAction = new RestrictAppAction(mContext, new RestrictAppTip( + BatteryTip.StateType.NEW, mAppInfos)); + mRestrictAppAction.mBatteryUtils = mBatteryUtils; + } + + @Test + public void testHandlePositiveAction() { + mRestrictAppAction.handlePositiveAction(); + + verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_1), + eq(AppOpsManager.MODE_IGNORED)); + verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_2), + eq(AppOpsManager.MODE_IGNORED)); + } + + +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java new file mode 100644 index 00000000000..a83a1587b64 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java @@ -0,0 +1,61 @@ +/* + * 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.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.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class UnrestrictAppTipTest { + private static final String PACKAGE_NAME = "com.android.app"; + + private UnrestrictAppTip mBatteryTip; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + AppInfo appInfo = new AppInfo.Builder() + .setPackageName(PACKAGE_NAME) + .build(); + mBatteryTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo); + } + + @Test + public void testParcelable() { + Parcel parcel = Parcel.obtain(); + mBatteryTip.writeToParcel(parcel, mBatteryTip.describeContents()); + parcel.setDataPosition(0); + + final UnrestrictAppTip parcelTip = new UnrestrictAppTip(parcel); + + assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.REMOVE_APP_RESTRICTION); + assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW); + assertThat(parcelTip.getPackageName()).isEqualTo(PACKAGE_NAME); + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java index 06910862d3b..b4169913a8d 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java @@ -27,6 +27,9 @@ import com.android.settings.wrapper.FingerprintManagerWrapper; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.HashMap; +import java.util.Map; + @Implements(Utils.class) public class ShadowUtils { @@ -34,6 +37,7 @@ public class ShadowUtils { private static boolean sIsUserAMonkey; private static boolean sIsDemoUser; private static ComponentName sDeviceOwnerComponentName; + private static Map sAppNameMap; @Implementation public static int enforceSameOwner(Context context, int userId) { @@ -89,4 +93,19 @@ public class ShadowUtils { public static int getManagedProfileId(UserManager um, int parentUserId) { return UserHandle.USER_NULL; } + + @Implementation + public static CharSequence getApplicationLabel(Context context, String packageName) { + if (sAppNameMap != null) { + return sAppNameMap.get(packageName); + } + return null; + } + + public static void setApplicationLabel(String packageName, String appLabel) { + if (sAppNameMap == null) { + sAppNameMap = new HashMap<>(); + } + sAppNameMap.put(packageName, appLabel); + } }