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

Commit 5bb2a87b authored by Fan Zhang's avatar Fan Zhang
Browse files

Create a page to manage dnd permission for individual app

- Change the original ZenAccessPage to
  - Remove the inline switch
  - Make the preference click target go into the new detail page
  - Some formatting/style change.

- Create a new detail page for zen access.
  - Exit if app didn't declare this permission
  - Preset the switch toggle to their current permission grant state
  - Move the warning dialog logic from ZenAccessSettings to here.

- Move some common functionality into ZenAccessController, a static
  helper class

Bug: 128547723
Test: robotest
Change-Id: I1ebb32396869d07ff4283b300bd716506298c9b5
parent 9f5e7b77
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -7939,6 +7939,9 @@
    <!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
    <string name="manage_zen_access_title">Do Not Disturb access</string>
    <!-- Button title that grants 'Do Not Disturb' permission to an app [CHAR_LIMIT=60]-->
    <string name="zen_access_detail_switch">Allow Do Not Disturb</string>
    <!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
    <string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
+27 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2019 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.
  -->

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:key="zen_access_permission_detail_settings"
    android:title="@string/manage_zen_access_title">

    <SwitchPreference
        android:key="zen_access_switch"
        android:title="@string/zen_access_detail_switch"/>

</PreferenceScreen>
 No newline at end of file
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.applications.specialaccess.zenaccess;

import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.text.TextUtils;

import androidx.appcompat.app.AlertDialog;

import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;

/**
 * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
 */
public class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
    static final String KEY_PKG = "p";
    static final String KEY_LABEL = "l";


    @Override
    public int getMetricsCategory() {
        return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
    }

    public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
        Bundle args = new Bundle();
        args.putString(KEY_PKG, pkg);
        args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
        setArguments(args);
        return this;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Bundle args = getArguments();
        final String pkg = args.getString(KEY_PKG);
        final String label = args.getString(KEY_LABEL);

        final String title = getResources().getString(
                R.string.zen_access_revoke_warning_dialog_title, label);
        final String summary = getResources()
                .getString(R.string.zen_access_revoke_warning_dialog_summary);
        return new AlertDialog.Builder(getContext())
                .setMessage(summary)
                .setTitle(title)
                .setCancelable(true)
                .setPositiveButton(R.string.okay,
                        (dialog, id) -> {
                            ZenAccessController.deleteRules(getContext(), pkg);
                            ZenAccessController.setAccess(getContext(), pkg, false);
                        })
                .setNegativeButton(R.string.cancel,
                        (dialog, id) -> {
                            // pass
                        })
                .create();
    }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.applications.specialaccess.zenaccess;

import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.text.TextUtils;

import androidx.appcompat.app.AlertDialog;

import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.notification.ZenAccessSettings;

/**
 * Warning dialog when allowing zen access warning about the privileges being granted.
 */
public class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
    static final String KEY_PKG = "p";
    static final String KEY_LABEL = "l";

    @Override
    public int getMetricsCategory() {
        return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
    }

    public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
        Bundle args = new Bundle();
        args.putString(KEY_PKG, pkg);
        args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
        setArguments(args);
        return this;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Bundle args = getArguments();
        final String pkg = args.getString(KEY_PKG);
        final String label = args.getString(KEY_LABEL);

        final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
                label);
        final String summary = getResources()
                .getString(R.string.zen_access_warning_dialog_summary);
        return new AlertDialog.Builder(getContext())
                .setMessage(summary)
                .setTitle(title)
                .setCancelable(true)
                .setPositiveButton(R.string.allow,
                        (dialog, id) -> ZenAccessController.setAccess(getContext(), pkg, true))
                .setNegativeButton(R.string.deny,
                        (dialog, id) -> {
                            // pass
                        })
                .create();
    }
}
+78 −1
Original line number Diff line number Diff line
@@ -17,12 +17,29 @@
package com.android.settings.applications.specialaccess.zenaccess;

import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;

import java.util.List;
import java.util.Set;

public class ZenAccessController extends BasePreferenceController {

    private static final String TAG = "ZenAccessController";

    private final ActivityManager mActivityManager;

    public ZenAccessController(Context context, String preferenceKey) {
@@ -32,8 +49,68 @@ public class ZenAccessController extends BasePreferenceController {

    @Override
    public int getAvailabilityStatus() {
        return !mActivityManager.isLowRamDevice()
        return isSupported(mActivityManager)
                ? AVAILABLE_UNSEARCHABLE
                : UNSUPPORTED_ON_DEVICE;
    }

    public static boolean isSupported(ActivityManager activityManager) {
        return !activityManager.isLowRamDevice();
    }

    public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
        final ArraySet<String> requestingPackages = new ArraySet<>();
        try {
            final String[] PERM = {
                    android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
            };
            final ParceledListSlice list = AppGlobals.getPackageManager()
                    .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
                            ActivityManager.getCurrentUser());
            final List<PackageInfo> pkgs = list.getList();
            if (pkgs != null) {
                for (PackageInfo info : pkgs) {
                    requestingPackages.add(info.packageName);
                }
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Cannot reach packagemanager", e);
        }
        return requestingPackages;
    }

    public static Set<String> getAutoApprovedPackages(Context context) {
        final Set<String> autoApproved = new ArraySet<>();
        autoApproved.addAll(context.getSystemService(NotificationManager.class)
                .getEnabledNotificationListenerPackages());
        return autoApproved;
    }

    public static boolean hasAccess(Context context, String pkg) {
        return context.getSystemService(
                NotificationManager.class).isNotificationPolicyAccessGrantedForPackage(pkg);
    }

    public static void setAccess(final Context context, final String pkg, final boolean access) {
        logSpecialPermissionChange(access, pkg, context);
        AsyncTask.execute(() -> {
            final NotificationManager mgr = context.getSystemService(NotificationManager.class);
            mgr.setNotificationPolicyAccessGranted(pkg, access);
        });
    }

    public static void deleteRules(final Context context, final String pkg) {
        AsyncTask.execute(() -> {
            final NotificationManager mgr = context.getSystemService(NotificationManager.class);
            mgr.removeAutomaticZenRules(pkg);
        });
    }

    @VisibleForTesting
    static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
        int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_DND_ALLOW
                : SettingsEnums.APP_SPECIAL_PERMISSION_DND_DENY;
        FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
                logCategory, packageName);
    }
}
Loading