Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3cf0490 authored by Wesley Wang's avatar Wesley Wang Committed by Android (Google) Code Review
Browse files

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
parents 5fb1b612 8ca6f317
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -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>
 No newline at end of file
+71 −1
Original line number Diff line number Diff line
@@ -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()));
    }
}
+121 −0
Original line number Diff line number Diff line
/*
 * 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));
    }
}
+85 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+88 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
Loading