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

Commit db35dce5 authored by Xinyi Mao's avatar Xinyi Mao Committed by Android (Google) Code Review
Browse files

Merge "[Expressive Battery] Migrate to androidx AlertDialog for...

Merge "[Expressive Battery] Migrate to androidx AlertDialog for RequestIgnoreBatteryOptimizations." into main
parents bbdbd5df 0ef3a39d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2046,7 +2046,7 @@
            android:name=".fuelgauge.RequestIgnoreBatteryOptimizations"
            android:label="@string/high_power_apps"
            android:exported="true"
            android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight">
            android:theme="@style/Transparent">
            <intent-filter android:priority="1">
                <action android:name="android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
                <category android:name="android.intent.category.DEFAULT" />
+78 −20
Original line number Diff line number Diff line
@@ -16,9 +16,13 @@

package com.android.settings.fuelgauge;

import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_POSITIVE;

import static com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNRESTRICTED;

import android.Manifest;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
@@ -30,41 +34,60 @@ import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;

import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.SettingsThemeHelper;

public class RequestIgnoreBatteryOptimizations extends AlertActivity
public class RequestIgnoreBatteryOptimizations extends FragmentActivity
        implements DialogInterface.OnClickListener {
    private static final String TAG = "RequestIgnoreBatteryOptimizations";
    private static final boolean DEBUG = false;

    @VisibleForTesting static BatteryOptimizeUtils sTestBatteryOptimizeUtils = null;

    @VisibleForTesting ApplicationInfo mApplicationInfo;
    @VisibleForTesting MetricsFeatureProvider mMetricsFeatureProvider;

    /** Request status of ignore battery optimizations dialog. */
    @VisibleForTesting
    static BatteryOptimizeUtils sTestBatteryOptimizeUtils = null;
    enum RequestStatus {
        UNKNOWN(0),
        ALREADY_PROMPTED(1),
        NO_PERMISSION(2),
        PACKAGE_NOT_EXIST(3);

    private ApplicationInfo mApplicationInfo;
        public final int value;

        RequestStatus(int value) {
            this.value = value;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
        getWindow()
                .addSystemFlags(
                        android.view.WindowManager.LayoutParams
                                .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
        if (SettingsThemeHelper.isExpressiveTheme(this)) {
            setTheme(R.style.Transparent_Expressive);
        }

        Uri data = getIntent().getData();
        if (data == null) {
            debugLog(
                    "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " + getIntent());
            finish();
            return;
        }
        final String packageName = data.getSchemeSpecificPart();
        final String packageName = data == null ? null : data.getSchemeSpecificPart();
        if (TextUtils.isEmpty(packageName)) {
            debugLog(
                    "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " + getIntent());
            logDialogAction(
                    SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                    RequestStatus.PACKAGE_NOT_EXIST.value);
            finish();
            return;
        }
@@ -73,6 +96,9 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity
        PowerManager power = getSystemService(PowerManager.class);
        if (power.isIgnoringBatteryOptimizations(packageName)) {
            debugLog("Not should prompt, already ignoring optimizations: " + packageName);
            logDialogAction(
                    SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                    RequestStatus.ALREADY_PROMPTED.value);
            finish();
            return;
        }
@@ -87,6 +113,9 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity
                            + packageName
                            + " does not hold permission "
                            + Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            logDialogAction(
                    SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                    RequestStatus.NO_PERMISSION.value);
            finish();
            return;
        }
@@ -95,24 +124,30 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity
            mApplicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            debugLog("Requested package doesn't exist: " + packageName);
            logDialogAction(
                    SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                    RequestStatus.PACKAGE_NOT_EXIST.value);
            finish();
            return;
        }

        final AlertController.AlertParams p = mAlertParams;
        final CharSequence appLabel =
                mApplicationInfo.loadSafeLabel(
                        getPackageManager(),
                        PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
                        PackageItemInfo.SAFE_LABEL_FLAG_TRIM
                                | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
        p.mTitle = getText(R.string.high_power_prompt_title);
        p.mMessage = getString(R.string.high_power_prompt_body, appLabel);
        p.mPositiveButtonText = getText(R.string.allow);
        p.mNegativeButtonText = getText(R.string.deny);
        p.mPositiveButtonListener = this;
        p.mNegativeButtonListener = this;
        setupAlert();
        AlertDialog dialog =
                new AlertDialog.Builder(this)
                        .setTitle(R.string.high_power_prompt_title)
                        .setMessage(getString(R.string.high_power_prompt_body, appLabel))
                        .setPositiveButton(R.string.allow, this)
                        .setNegativeButton(R.string.deny, this)
                        .setOnDismissListener(this::onDismissDialog)
                        .create();
        logDialogAction(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_SHOW, mApplicationInfo.uid);
        dialog.show();
    }

    @Override
@@ -128,12 +163,35 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity
                                        mApplicationInfo.packageName);
                batteryOptimizeUtils.setAppUsageState(
                        MODE_UNRESTRICTED, Action.APPLY, /* forceMode= */ true);
                logDialogAction(
                        SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_ALLOW,
                        mApplicationInfo.uid);
                dialog.dismiss();
                break;
            case BUTTON_NEGATIVE:
                logDialogAction(
                        SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_DENY,
                        mApplicationInfo.uid);
                dialog.dismiss();
                break;
        }
    }

    private void onDismissDialog(DialogInterface dialog) {
        logDialogAction(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_DISMISS, mApplicationInfo.uid);
        finish();
    }

    private void logDialogAction(int action, int value) {
        mMetricsFeatureProvider.action(
                /* attribution= */ SettingsEnums.DIALOG_REQUEST_IGNORE_BATTERY_OPTIMIZE,
                /* action= */ action,
                /* pageId= */ SettingsEnums.DIALOG_REQUEST_IGNORE_BATTERY_OPTIMIZE,
                /* key= */ TAG,
                /* value= */ value);
    }

    private static void debugLog(String debugContent) {
        if (DEBUG) Log.w(TAG, debugContent);
    }
+53 −4
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;

import android.Manifest;
import android.app.AppOpsManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -43,6 +44,8 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;

import com.android.settings.fuelgauge.RequestIgnoreBatteryOptimizations.RequestStatus;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;

@@ -58,6 +61,7 @@ import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@@ -66,28 +70,38 @@ public class RequestIgnoreBatteryOptimizationsTest {
    private static final int UID = 12345;
    private static final String PACKAGE_NAME = "com.android.app";
    private static final String UNKNOWN_PACKAGE_NAME = "com.android.unknown";
    private static final String METRICS_KEY = "RequestIgnoreBatteryOptimizations";
    private static final String PACKAGE_LABEL = "app";
    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();

    private Context mContext;
    private FakeFeatureFactory mFeatureFactory;
    private RequestIgnoreBatteryOptimizations mActivity;
    private BatteryOptimizeUtils mBatteryOptimizeUtils;
    private PowerAllowlistBackend mPowerAllowlistBackend;
    private ActivityController<RequestIgnoreBatteryOptimizations> mActivityController;

    @Mock private PowerManager mMockPowerManager;
    @Mock private PackageManager mMockPackageManager;
    @Mock private ApplicationInfo mMockApplicationInfo;
    @Mock private BatteryUtils mMockBatteryUtils;
    @Mock private DialogInterface mMockDialog;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        mActivity = spy(Robolectric.setupActivity(RequestIgnoreBatteryOptimizations.class));
        mFeatureFactory = FakeFeatureFactory.setupForTest();
        mActivityController = Robolectric.buildActivity(RequestIgnoreBatteryOptimizations.class);
        mActivity = spy(mActivityController.get());
        mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME));
        mPowerAllowlistBackend = spy(PowerAllowlistBackend.getInstance(mContext));
        mBatteryOptimizeUtils.mPowerAllowListBackend = mPowerAllowlistBackend;
        mBatteryOptimizeUtils.mBatteryUtils = mMockBatteryUtils;
        mActivity.mApplicationInfo = mMockApplicationInfo;
        mActivity.mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
        mMockApplicationInfo.uid = UID;
        doNothing().when(mMockDialog).dismiss();
        RequestIgnoreBatteryOptimizations.sTestBatteryOptimizeUtils = mBatteryOptimizeUtils;

        when(mActivity.getApplicationContext()).thenReturn(mContext);
@@ -126,6 +140,7 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity, never()).finish();
        assertDialogMetrics(SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_SHOW, UID);
    }

    @Test
@@ -135,6 +150,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity).finish();
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                RequestStatus.PACKAGE_NOT_EXIST.value);
    }

    @Test
@@ -144,6 +162,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity).finish();
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                RequestStatus.PACKAGE_NOT_EXIST.value);
    }

    @Test
@@ -154,6 +175,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity).finish();
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                RequestStatus.ALREADY_PROMPTED.value);
    }

    @Test
@@ -167,6 +191,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity).finish();
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                RequestStatus.NO_PERMISSION.value);
    }

    @Test
@@ -176,20 +203,26 @@ public class RequestIgnoreBatteryOptimizationsTest {
        mActivity.onCreate(new Bundle());

        verify(mActivity).finish();
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_FAIL_SHOW,
                RequestStatus.PACKAGE_NOT_EXIST.value);
    }

    @Test
    public void onClick_clickNegativeButton_doNothing() {
        mActivity.onClick(null, DialogInterface.BUTTON_NEGATIVE);
        mActivity.onClick(mMockDialog, DialogInterface.BUTTON_NEGATIVE);

        verifyNoInteractions(mBatteryOptimizeUtils);
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_DENY, UID);
        verify(mMockDialog).dismiss();
    }

    @Test
    public void onClick_clickPositiveButtonWithUnrestrictedMode_addAllowlist() {
        when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(MODE_UNRESTRICTED);

        mActivity.onClick(null, DialogInterface.BUTTON_POSITIVE);
        mActivity.onClick(mMockDialog, DialogInterface.BUTTON_POSITIVE);

        verify(mBatteryOptimizeUtils)
                .setAppUsageState(
@@ -198,6 +231,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
                        /* forceMode= */ true);
        verify(mPowerAllowlistBackend).addApp(PACKAGE_NAME, UID);
        verify(mMockBatteryUtils).setForceAppStandby(UID, PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_ALLOW, UID);
        verify(mMockDialog).dismiss();
    }

    @Test
@@ -205,7 +241,7 @@ public class RequestIgnoreBatteryOptimizationsTest {
        when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(MODE_RESTRICTED);
        doNothing().when(mMockBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt());

        mActivity.onClick(null, DialogInterface.BUTTON_POSITIVE);
        mActivity.onClick(mMockDialog, DialogInterface.BUTTON_POSITIVE);

        verify(mBatteryOptimizeUtils)
                .setAppUsageState(
@@ -214,6 +250,9 @@ public class RequestIgnoreBatteryOptimizationsTest {
                        /* forceMode= */ true);
        verify(mPowerAllowlistBackend).addApp(PACKAGE_NAME, UID);
        verify(mMockBatteryUtils).setForceAppStandby(UID, PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
        assertDialogMetrics(
                SettingsEnums.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZE_ALLOW, UID);
        verify(mMockDialog).dismiss();
    }

    private Intent createIntent(String packageName) {
@@ -221,4 +260,14 @@ public class RequestIgnoreBatteryOptimizationsTest {
        intent.setData(new Uri.Builder().scheme("package").opaquePart(packageName).build());
        return intent;
    }

    private void assertDialogMetrics(final int action, final int value) {
        verify(mFeatureFactory.metricsFeatureProvider)
                .action(
                        SettingsEnums.DIALOG_REQUEST_IGNORE_BATTERY_OPTIMIZE,
                        action,
                        SettingsEnums.DIALOG_REQUEST_IGNORE_BATTERY_OPTIMIZE,
                        METRICS_KEY,
                        value);
    }
}