Merge changes Iced0fa59,Ic354ac91,I0b4cb6ca,I9c1d3761 into sc-dev

* changes:
  Add footer to app usage page
  Update app usage page with 3 radio button control
  Add 3 controller for app usage page radio buttons
  Add utils class for app usage operation
This commit is contained in:
Wesley Wang
2021-04-19 10:15:49 +00:00
committed by Android (Google) Code Review
11 changed files with 1041 additions and 1 deletions

View File

@@ -30,6 +30,30 @@
android:key="action_buttons"
android:order="-9999"/>
<PreferenceCategory
android:title="@string/battery_detail_manage_title"
settings:allowDividerAbove="true">
<com.android.settingslib.widget.RadioButtonPreference
android:key="unrestricted_pref"
android:summary="@string/manager_battery_usage_unrestricted_summary"
android:title="@string/manager_battery_usage_unrestricted_title"
settings:controller="com.android.settings.fuelgauge.UnrestrictedPreferenceController"/>
<com.android.settingslib.widget.RadioButtonPreference
android:key="optimized_pref"
android:summary="@string/manager_battery_usage_optimized_summary"
android:title="@string/manager_battery_usage_optimized_title"
settings:controller="com.android.settings.fuelgauge.OptimizedPreferenceController"/>
<com.android.settingslib.widget.RadioButtonPreference
android:key="restricted_pref"
android:summary="@string/manager_battery_usage_restricted_summary"
android:title="@string/restricted_true_label"
settings:controller="com.android.settings.fuelgauge.RestrictedPreferenceController"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/battery_detail_manage_title"
settings:allowDividerAbove="true">
@@ -63,4 +87,11 @@
</PreferenceCategory>
<com.android.settingslib.widget.FooterPreference
android:order="100"
android:key="app_usage_footer_preference"
android:title="@string/manager_battery_usage_footer"
android:selectable="true"
settings:searchable="false"/>
</PreferenceScreen>

View File

@@ -25,6 +25,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -48,6 +49,7 @@ import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.widget.RadioButtonPreference;
import java.util.ArrayList;
import java.util.List;
@@ -60,7 +62,7 @@ import java.util.List;
*/
public class AdvancedPowerUsageDetail extends DashboardFragment implements
ButtonActionDialogFragment.AppButtonsDialogListener,
BatteryTipPreferenceController.BatteryTipListener {
BatteryTipPreferenceController.BatteryTipListener, RadioButtonPreference.OnClickListener {
public static final String TAG = "AdvancedPowerDetail";
public static final String EXTRA_UID = "extra_uid";
@@ -75,6 +77,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
private static final String KEY_PREF_BACKGROUND = "app_usage_background";
private static final String KEY_PREF_HEADER = "header_view";
private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
private static final String KEY_PREF_RESTRICTED = "restricted_pref";
private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference";
private static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
@@ -87,13 +93,26 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
ApplicationsState.AppEntry mAppEntry;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting
Preference mForegroundPreference;
@VisibleForTesting
Preference mBackgroundPreference;
@VisibleForTesting
Preference mFooterPreference;
@VisibleForTesting
RadioButtonPreference mRestrictedPreference;
@VisibleForTesting
RadioButtonPreference mOptimizePreference;
@VisibleForTesting
RadioButtonPreference mUnrestrictedPreference;
private AppButtonsPreferenceController mAppButtonsPreferenceController;
private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
private UnrestrictedPreferenceController mUnrestrictedPreferenceController;
private OptimizedPreferenceController mOptimizedPreferenceController;
private RestrictedPreferenceController mRestrictedPreferenceController;
private String mPackageName;
@@ -174,8 +193,19 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED);
mUnrestrictedPreference.setOnClickListener(this);
mOptimizePreference.setOnClickListener(this);
mRestrictedPreference.setOnClickListener(this);
mBatteryOptimizeUtils = new BatteryOptimizeUtils(
getContext(), getArguments().getInt(EXTRA_UID), mPackageName);
if (mPackageName != null) {
mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
}
@@ -241,6 +271,26 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
backgroundTimeMs,
/* withSeconds */ false,
/* collapseTimeUnit */ false)));
final String stateString;
final String footerString;
//TODO(b/178197718) Update strings
if (!mBatteryOptimizeUtils.isValidPackageName()) {
//Present optimized only string when the package name is invalid.
stateString = context.getString(R.string.manager_battery_usage_optimized_title);
footerString = context.getString(
R.string.manager_battery_usage_footer_limited, stateString);
} else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
//Present unrestricted only string when the package is system or default active app.
stateString = context.getString(R.string.manager_battery_usage_unrestricted_title);
footerString = context.getString(
R.string.manager_battery_usage_footer_limited, stateString);
} else {
//Present default string to normal app.
footerString = context.getString(R.string.manager_battery_usage_footer);
}
mFooterPreference.setTitle(Html.fromHtml(footerString, Html.FROM_HTML_MODE_COMPACT));
}
@Override
@@ -274,6 +324,15 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
(SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName, mState,
REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
controllers.add(mAppButtonsPreferenceController);
mUnrestrictedPreferenceController =
new UnrestrictedPreferenceController(context, uid, packageName);
mOptimizedPreferenceController =
new OptimizedPreferenceController(context, uid, packageName);
mRestrictedPreferenceController =
new RestrictedPreferenceController(context, uid, packageName);
controllers.add(mUnrestrictedPreferenceController);
controllers.add(mOptimizedPreferenceController);
controllers.add(mRestrictedPreferenceController);
return controllers;
}
@@ -298,4 +357,15 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
mBackgroundActivityPreferenceController.updateSummary(
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
}
@Override
public void onRadioButtonClicked(RadioButtonPreference selected) {
updatePreferenceState(mUnrestrictedPreference, selected.getKey());
updatePreferenceState(mOptimizePreference, selected.getKey());
updatePreferenceState(mRestrictedPreference, selected.getKey());
}
private void updatePreferenceState(RadioButtonPreference preference, String selectedKey) {
preference.setChecked(selectedKey.equals(preference.getKey()));
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2021 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;
import android.app.AppOpsManager;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
/** A utility class for application usage operation. */
public class BatteryOptimizeUtils {
private static final String TAG = "BatteryOptimizeUtils";
@VisibleForTesting AppOpsManager mAppOpsManager;
@VisibleForTesting BatteryUtils mBatteryUtils;
@VisibleForTesting PowerAllowlistBackend mPowerAllowListBackend;
private final String mPackageName;
private final int mUid;
private int mMode;
private boolean mAllowListed;
/**
* Usage type of application.
*/
public enum AppUsageState {
UNKNOWN,
RESTRICTED,
UNRESTRICTED,
OPTIMIZED,
}
public BatteryOptimizeUtils(Context context, int uid, String packageName) {
mUid = uid;
mPackageName = packageName;
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mBatteryUtils = BatteryUtils.getInstance(context);
mPowerAllowListBackend = PowerAllowlistBackend.getInstance(context);
mMode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName);
mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName);
}
public AppUsageState getAppUsageState() {
refreshState();
if (!mAllowListed && mMode == AppOpsManager.MODE_IGNORED) {
return AppUsageState.RESTRICTED;
} else if (mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) {
return AppUsageState.UNRESTRICTED;
} else if (!mAllowListed && mMode == AppOpsManager.MODE_ALLOWED) {
return AppUsageState.OPTIMIZED;
} else {
Log.d(TAG, "get unknown app usage state.");
return AppUsageState.UNKNOWN;
}
}
public void setAppUsageState(AppUsageState state) {
switch (state) {
case RESTRICTED:
mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_IGNORED);
mPowerAllowListBackend.removeApp(mPackageName);
break;
case UNRESTRICTED:
mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_ALLOWED);
mPowerAllowListBackend.addApp(mPackageName);
break;
case OPTIMIZED:
mBatteryUtils.setForceAppStandby(mUid, mPackageName, AppOpsManager.MODE_ALLOWED);
mPowerAllowListBackend.removeApp(mPackageName);
break;
default:
Log.d(TAG, "set unknown app usage state.");
}
}
/**
* Return {@code true} if package name is valid (can get an uid).
*/
public boolean isValidPackageName() {
return mBatteryUtils.getPackageUid(mPackageName) != BatteryUtils.UID_NULL;
}
/**
* Return {@code true} if this package is system or default active app.
*/
public boolean isSystemOrDefaultApp() {
mPowerAllowListBackend.refreshList();
return mPowerAllowListBackend.isSysAllowlisted(mPackageName)
|| mPowerAllowListBackend.isDefaultActiveApp(mPackageName);
}
private void refreshState() {
mPowerAllowListBackend.refreshList();
mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName);
mMode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName);
Log.d(TAG, String.format("refresh %s state, allowlisted = %s, mode = %d",
mPackageName,
mAllowListed,
mMode));
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2021 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;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.OPTIMIZED;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.RadioButtonPreference;
public class OptimizedPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
private static final String TAG = "OPTIMIZED_PREF";
@VisibleForTesting String KEY_OPTIMIZED_PREF = "optimized_pref";
@VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
public OptimizedPreferenceController(Context context, int uid, String packageName) {
super(context);
mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
if (!mBatteryOptimizeUtils.isValidPackageName()) {
Log.d(TAG, "invalid package name, optimized states only");
preference.setEnabled(true);
((RadioButtonPreference) preference).setChecked(true);
return;
}
if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
Log.d(TAG, "is system or default app, disable pref");
((RadioButtonPreference) preference).setChecked(false);
preference.setEnabled(false);
} else if (mBatteryOptimizeUtils.getAppUsageState() == OPTIMIZED) {
Log.d(TAG, "is optimized states");
((RadioButtonPreference) preference).setChecked(true);
} else {
((RadioButtonPreference) preference).setChecked(false);
}
}
@Override
public String getPreferenceKey() {
return KEY_OPTIMIZED_PREF;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!KEY_OPTIMIZED_PREF.equals(preference.getKey())) {
return false;
}
mBatteryOptimizeUtils.setAppUsageState(OPTIMIZED);
Log.d(TAG, "Set optimized");
return true;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2021 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;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.RESTRICTED;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.RadioButtonPreference;
public class RestrictedPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
private static final String TAG = "RESTRICTED_PREF";
@VisibleForTesting String KEY_RESTRICTED_PREF = "restricted_pref";
@VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
public RestrictedPreferenceController(Context context, int uid, String packageName) {
super(context);
mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
}
@Override
public void updateState(Preference preference) {
if (!mBatteryOptimizeUtils.isValidPackageName()) {
Log.d(TAG, "invalid package name, disable pref");
preference.setEnabled(false);
return;
} else {
preference.setEnabled(true);
}
if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
Log.d(TAG, "is system or default app, disable pref");
((RadioButtonPreference) preference).setChecked(false);
preference.setEnabled(false);
} else if (mBatteryOptimizeUtils.getAppUsageState() == RESTRICTED) {
Log.d(TAG, "is restricted states");
((RadioButtonPreference) preference).setChecked(true);
} else {
((RadioButtonPreference) preference).setChecked(false);
}
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_RESTRICTED_PREF;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!KEY_RESTRICTED_PREF.equals(preference.getKey())) {
return false;
}
mBatteryOptimizeUtils.setAppUsageState(RESTRICTED);
Log.d(TAG, "Set restricted");
return true;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2021 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;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.UNRESTRICTED;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.RadioButtonPreference;
public class UnrestrictedPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
private static final String TAG = "UNRESTRICTED_PREF";
@VisibleForTesting String KEY_UNRESTRICTED_PREF = "unrestricted_pref";
@VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
public UnrestrictedPreferenceController(Context context, int uid, String packageName) {
super(context);
mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName);
}
@Override
public void updateState(Preference preference) {
if (!mBatteryOptimizeUtils.isValidPackageName()) {
Log.d(TAG, "invalid package name, disable pref");
preference.setEnabled(false);
return;
} else {
preference.setEnabled(true);
}
if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
Log.d(TAG, "is system or default app, unrestricted states only");
((RadioButtonPreference) preference).setChecked(true);
} else if (mBatteryOptimizeUtils.getAppUsageState() == UNRESTRICTED) {
Log.d(TAG, "is unrestricted states");
((RadioButtonPreference) preference).setChecked(true);
} else {
((RadioButtonPreference) preference).setChecked(false);
}
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_UNRESTRICTED_PREF;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!KEY_UNRESTRICTED_PREF.equals(preference.getKey())) {
return false;
}
mBatteryOptimizeUtils.setAppUsageState(UNRESTRICTED);
Log.d(TAG, "Set unrestricted");
return true;
}
}

View File

@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -58,6 +59,7 @@ import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.widget.RadioButtonPreference;
import org.junit.After;
import org.junit.Before;
@@ -96,6 +98,9 @@ public class AdvancedPowerUsageDetailTest {
private static final long PROCSTATE_TOP_TIME_US = PROCSTATE_TOP_TIME_MS * 1000;
private static final long PHONE_FOREGROUND_TIME_MS = 250 * 1000;
private static final long PHONE_BACKGROUND_TIME_MS = 0;
private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
private static final String KEY_PREF_RESTRICTED = "restricted_pref";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private FragmentActivity mActivity;
@@ -121,9 +126,15 @@ public class AdvancedPowerUsageDetailTest {
private BatteryStats.Timer mForegroundActivityTimer;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private BatteryOptimizeUtils mBatteryOptimizeUtils;
private Context mContext;
private Preference mForegroundPreference;
private Preference mBackgroundPreference;
private Preference mFooterPreference;
private RadioButtonPreference mRestrictedPreference;
private RadioButtonPreference mOptimizePreference;
private RadioButtonPreference mUnrestrictedPreference;
private AdvancedPowerUsageDetail mFragment;
private SettingsActivity mTestActivity;
@@ -170,6 +181,7 @@ public class AdvancedPowerUsageDetailTest {
mFragment.mHeaderPreference = mHeaderPreference;
mFragment.mState = mState;
mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils;
mAppEntry.info = mock(ApplicationInfo.class);
mTestActivity = spy(new SettingsActivity());
@@ -194,8 +206,16 @@ public class AdvancedPowerUsageDetailTest {
mForegroundPreference = new Preference(mContext);
mBackgroundPreference = new Preference(mContext);
mFooterPreference = new Preference(mContext);
mRestrictedPreference = new RadioButtonPreference(mContext);
mOptimizePreference = new RadioButtonPreference(mContext);
mUnrestrictedPreference = new RadioButtonPreference(mContext);
mFragment.mForegroundPreference = mForegroundPreference;
mFragment.mBackgroundPreference = mBackgroundPreference;
mFragment.mFooterPreference = mFooterPreference;
mFragment.mRestrictedPreference = mRestrictedPreference;
mFragment.mOptimizePreference = mOptimizePreference;
mFragment.mUnrestrictedPreference = mUnrestrictedPreference;
}
@After
@@ -352,4 +372,48 @@ public class AdvancedPowerUsageDetailTest {
assertThat(mForegroundPreference.getSummary().toString()).isEqualTo("Used for 0 min");
assertThat(mBackgroundPreference.getSummary().toString()).isEqualTo("Active for 0 min");
}
@Test
public void testInitPreference_isValidPackageName_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
mFragment.initPreference();
assertThat(mFooterPreference.getTitle().toString())
.isEqualTo("This app requires Optimized battery usage.");
}
@Test
public void testInitPreference_isSystemOrDefaultApp_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
mFragment.initPreference();
assertThat(mFooterPreference.getTitle()
.toString()).isEqualTo("This app requires Unrestricted battery usage.");
}
@Test
public void testInitPreference_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
mFragment.initPreference();
assertThat(mFooterPreference.getTitle().toString())
.isEqualTo("Changing how an app uses your battery can affect its performance.");
}
@Test
public void testOnRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() {
mOptimizePreference.setKey(KEY_PREF_OPTIMIZED);
mRestrictedPreference.setKey(KEY_PREF_RESTRICTED);
mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED);
mFragment.onRadioButtonClicked(mOptimizePreference);
assertThat(mOptimizePreference.isChecked()).isTrue();
assertThat(mRestrictedPreference.isChecked()).isFalse();
assertThat(mUnrestrictedPreference.isChecked()).isFalse();
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2021 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;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.OPTIMIZED;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.RESTRICTED;
import static com.android.settings.fuelgauge.BatteryOptimizeUtils.AppUsageState.UNRESTRICTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BatteryOptimizeUtilsTest {
private static final int UID = 12345;
private static final String PACKAGE_NAME = "com.android.app";
@Mock BatteryUtils mockBatteryUtils;
@Mock AppOpsManager mockAppOpsManager;
@Mock PowerAllowlistBackend mockBackend;
private Context mContext;
private BatteryOptimizeUtils mBatteryOptimizeUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME));
mBatteryOptimizeUtils.mAppOpsManager = mockAppOpsManager;
mBatteryOptimizeUtils.mBatteryUtils = mockBatteryUtils;
mBatteryOptimizeUtils.mPowerAllowListBackend = mockBackend;
}
@Test
public void testGetAppUsageState_returnRestricted() {
when(mockBackend.isAllowlisted(anyString())).thenReturn(false);
when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_IGNORED);
assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(RESTRICTED);
}
@Test
public void testGetAppUsageState_returnUnrestricted() {
when(mockBackend.isAllowlisted(anyString())).thenReturn(true);
when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(UNRESTRICTED);
}
@Test
public void testGetAppUsageState_returnOptimized() {
when(mockBackend.isAllowlisted(anyString())).thenReturn(false);
when(mockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
assertThat(mBatteryOptimizeUtils.getAppUsageState()).isEqualTo(OPTIMIZED);
}
@Test
public void testIsSystemOrDefaultApp_isSystemOrDefaultApp_returnTrue() {
when(mockBackend.isAllowlisted(anyString())).thenReturn(true);
when(mockBackend.isDefaultActiveApp(anyString())).thenReturn(true);
assertThat(mBatteryOptimizeUtils.isSystemOrDefaultApp()).isTrue();
}
@Test
public void testIsSystemOrDefaultApp_notSystemOrDefaultApp_returnFalse() {
assertThat(mBatteryOptimizeUtils.isSystemOrDefaultApp()).isFalse();
}
@Test
public void testIsValidPackageName_InvalidPackageName_returnFalse() {
final BatteryOptimizeUtils testBatteryOptimizeUtils =
new BatteryOptimizeUtils(mContext, UID, null);
assertThat(testBatteryOptimizeUtils.isValidPackageName()).isFalse();
}
@Test
public void testIsValidPackageName_validPackageName_returnTrue() {
assertThat(mBatteryOptimizeUtils.isValidPackageName()).isTrue();
}
@Test
public void testSetAppUsageState_Restricted_verifyAction() {
mBatteryOptimizeUtils.setAppUsageState(RESTRICTED);
verify(mockBatteryUtils).setForceAppStandby(UID,
PACKAGE_NAME, AppOpsManager.MODE_IGNORED);
verify(mockBackend).removeApp(PACKAGE_NAME);
}
@Test
public void testSetAppUsageState_Unrestricted_verifyAction() {
mBatteryOptimizeUtils.setAppUsageState(UNRESTRICTED);
verify(mockBatteryUtils).setForceAppStandby(UID,
PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
verify(mockBackend).addApp(PACKAGE_NAME);
}
@Test
public void testSetAppUsageState_Optimized_verifyAction() {
mBatteryOptimizeUtils.setAppUsageState(OPTIMIZED);
verify(mockBatteryUtils).setForceAppStandby(UID,
PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
verify(mockBackend).removeApp(PACKAGE_NAME);
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2021 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.android.settingslib.widget.RadioButtonPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class OptimizedPreferenceControllerTest {
private static final int UID = 12345;
private static final String PACKAGE_NAME = "com.android.app";
private OptimizedPreferenceController mController;
private RadioButtonPreference mPreference;
@Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new OptimizedPreferenceController(
RuntimeEnvironment.application, UID, PACKAGE_NAME);
mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
}
@Test
public void testUpdateState_invalidPackage_prefEnabled() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void testUpdateState_isOptimizedStates_prefChecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
BatteryOptimizeUtils.AppUsageState.OPTIMIZED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_prefUnchecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
mPreference.setKey(mController.KEY_OPTIMIZED_PREF);
mController.handlePreferenceTreeClick(mPreference);
verify(mockBatteryOptimizeUtils).setAppUsageState(
BatteryOptimizeUtils.AppUsageState.OPTIMIZED);
}
@Test
public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
mController.handlePreferenceTreeClick(mPreference);
verifyZeroInteractions(mockBatteryOptimizeUtils);
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.android.settingslib.widget.RadioButtonPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceControllerTest {
private static final int UID = 12345;
private static final String PACKAGE_NAME = "com.android.app";
private RestrictedPreferenceController mController;
private RadioButtonPreference mPreference;
@Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new RestrictedPreferenceController(
RuntimeEnvironment.application, UID, PACKAGE_NAME);
mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
}
@Test
public void testUpdateState_isValidPackage_prefEnabled() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void testUpdateState_invalidPackage_prefDisabled() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void testUpdateState_isRestrictedStates_prefChecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
BatteryOptimizeUtils.AppUsageState.RESTRICTED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_prefUnchecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
mPreference.setKey(mController.KEY_RESTRICTED_PREF);
mController.handlePreferenceTreeClick(mPreference);
verify(mockBatteryOptimizeUtils).setAppUsageState(
BatteryOptimizeUtils.AppUsageState.RESTRICTED);
}
@Test
public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
mController.handlePreferenceTreeClick(mPreference);
verifyZeroInteractions(mockBatteryOptimizeUtils);
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.android.settingslib.widget.RadioButtonPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class UnrestrictedPreferenceControllerTest {
private static final int UID = 12345;
private static final String PACKAGE_NAME = "com.android.app";
private UnrestrictedPreferenceController mController;
private RadioButtonPreference mPreference;
@Mock BatteryOptimizeUtils mockBatteryOptimizeUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new UnrestrictedPreferenceController(
RuntimeEnvironment.application, UID, PACKAGE_NAME);
mPreference = new RadioButtonPreference(RuntimeEnvironment.application);
mController.mBatteryOptimizeUtils = mockBatteryOptimizeUtils;
}
@Test
public void testUpdateState_isValidPackage_prefEnabled() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void testUpdateState_invalidPackage_prefDisabled() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_isUnrestrictedStates_prefChecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mockBatteryOptimizeUtils.getAppUsageState()).thenReturn(
BatteryOptimizeUtils.AppUsageState.UNRESTRICTED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_prefUnchecked() {
when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() {
mPreference.setKey(mController.KEY_UNRESTRICTED_PREF);
mController.handlePreferenceTreeClick(mPreference);
verify(mockBatteryOptimizeUtils).setAppUsageState(
BatteryOptimizeUtils.AppUsageState.UNRESTRICTED);
}
@Test
public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() {
mController.handlePreferenceTreeClick(mPreference);
verifyZeroInteractions(mockBatteryOptimizeUtils);
}
}