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); + } }