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

Commit be647ab0 authored by Alexander Roederer's avatar Alexander Roederer Committed by Android (Google) Code Review
Browse files

Merge changes from topic "b308819928-ModesSettingsAppBreakthroughPage" into main

* changes:
  Modify Summary for Mode's Apps settings page
  Adds summary helper for apps subtitle
parents 68953d33 41902bb0
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -9214,6 +9214,16 @@
    <string name="zen_mode_apps_none_apps">None</string>
    <!-- [CHAR LIMIT=60] Zen mode settings: all apps will be able to bypass dnd -->
    <string name="zen_mode_apps_all_apps">All</string>
    <!-- [CHAR LIMIT=NONE] Zen mode settings: Lists apps that can bypass DND. For example, "Nest, Messages, and 2 more can interrupt". -->
    <string name="zen_mode_apps_subtext">
        {count, plural, offset:2
            =0    {No apps can interrupt}
            =1    {{app_1} can interrupt}
            =2    {{app_1} and {app_2} can interrupt}
            =3    {{app_1}, {app_2}, and {app_3} can interrupt}
            other {{app_1}, {app_2}, and # more can interrupt}
        }
    </string>
    <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND -->
    <string name="zen_mode_bypassing_apps">Allow apps to override</string>
+13 −0
Original line number Diff line number Diff line
@@ -357,6 +357,19 @@ public class NotificationBackend {
        }
    }

    /**
     * Returns all of a user's packages that have at least one channel that will bypass DND
     */
    public List<String> getPackagesBypassingDnd(int userId,
            boolean includeConversationChannels) {
        try {
            return sINM.getPackagesBypassingDnd(userId, includeConversationChannels);
        } catch (Exception e) {
            Log.w(TAG, "Error calling NoMan", e);
            return new ArrayList<>();
        }
    }

    public void updateChannel(String pkg, int uid, NotificationChannel channel) {
        try {
            sINM.updateNotificationChannelForPackage(pkg, uid, channel);
+103 −4
Original line number Diff line number Diff line
@@ -20,23 +20,44 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I

import android.content.Context;
import android.os.Bundle;
import android.util.ArraySet;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;

import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.applications.ApplicationsState;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Preference with a link and summary about what apps can break through the mode
 */
public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {
class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {

    private static final String TAG = "ZenModeAppsLinkPreferenceController";

    private final ZenModeSummaryHelper mSummaryHelper;
    private ApplicationsState.Session mAppSession;
    private NotificationBackend mNotificationBackend = new NotificationBackend();
    private ZenMode mZenMode;
    private Preference mPreference;

    public ZenModeAppsLinkPreferenceController(Context context, String key,
            ZenModesBackend backend) {
    ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
            ApplicationsState applicationsState, ZenModesBackend backend) {
        super(context, key, backend);
        mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
        if (applicationsState != null && host != null) {
            mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
        }
    }

    @Override
@@ -49,6 +70,84 @@ public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferen
                .setSourceMetricsCategory(0)
                .setArguments(bundle)
                .toIntent());
        preference.setSummary(mSummaryHelper.getAppsSummary(zenMode));
        mZenMode = zenMode;
        mPreference = preference;
        triggerUpdateAppsBypassingDndSummaryText();
    }

    private void triggerUpdateAppsBypassingDndSummaryText() {
        if (mAppSession == null) {
            return;
        }

        ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures()
                && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()
                ? ApplicationsState.FILTER_ENABLED_NOT_QUIET
                : ApplicationsState.FILTER_ALL_ENABLED;
        // We initiate a rebuild in the background here. Once the rebuild is completed,
        // the onRebuildComplete() callback will be invoked, which will trigger the summary text
        // to be initialized.
        mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR, false);
    }

    private void updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps) {
        Set<String> appNames = getAppsBypassingDnd(apps);
        mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, appNames));
    }

    @VisibleForTesting
    ArraySet<String> getAppsBypassingDnd(@NonNull List<ApplicationsState.AppEntry> apps) {
        ArraySet<String> appsBypassingDnd = new ArraySet<>();

        Map<String, String> pkgLabelMap = new HashMap<String, String>();
        for (ApplicationsState.AppEntry entry : apps) {
            if (entry.info != null) {
                pkgLabelMap.put(entry.info.packageName, entry.label);
            }
        }
        for (String pkg : mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(),
                /* includeConversationChannels= */ false)) {
            // Settings may hide some packages from the user, so if they're not present here
            // we skip displaying them, even if they bypass dnd.
            if (pkgLabelMap.get(pkg) == null) {
                continue;
            }
            appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(pkgLabelMap.get(pkg)));
        }
        return appsBypassingDnd;
    }

    @VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks =
            new ApplicationsState.Callbacks() {

                @Override
                public void onRunningStateChanged(boolean running) { }

                @Override
                public void onPackageListChanged() {
                    triggerUpdateAppsBypassingDndSummaryText();
                }

                @Override
                public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
                    updateAppsBypassingDndSummaryText(apps);
                }

                @Override
                public void onPackageIconChanged() { }

                @Override
                public void onPackageSizeChanged(String packageName) { }

                @Override
                public void onAllSizesComputed() { }

                @Override
                public void onLauncherInfoChanged() { }

                @Override
                public void onLoadEntriesCompleted() {
                    triggerUpdateAppsBypassingDndSummaryText();
                }
            };
}
+5 −1
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.settings.notification.modes;

import android.app.Application;
import android.app.AutomaticZenRule;
import android.app.settings.SettingsEnums;
import android.content.Context;

import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;

import java.util.ArrayList;
@@ -42,7 +44,9 @@ public class ZenModeFragment extends ZenModeFragmentBase {
        prefControllers.add(new ZenModePeopleLinkPreferenceController(
                context, "zen_mode_people", mBackend));
        prefControllers.add(new ZenModeAppsLinkPreferenceController(
                context, "zen_mode_apps", mBackend));
                context, "zen_mode_apps", this,
                ApplicationsState.getInstance((Application) context.getApplicationContext()),
                mBackend));
        prefControllers.add(new ZenModeOtherLinkPreferenceController(
                context, "zen_other_settings", mBackend));
        prefControllers.add(new ZenModeDisplayLinkPreferenceController(
+41 −4
Original line number Diff line number Diff line
@@ -43,13 +43,18 @@ import android.icu.text.MessageFormat;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.settings.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

class ZenModeSummaryHelper {
@@ -397,12 +402,13 @@ class ZenModeSummaryHelper {
    }

    /**
     * Generates a summary to display under the top level "Apps" preference for a mode.
     * Generates a summary to display under the top level "Apps" preference for a mode, based
     * on the given mode and provided set of apps.
     */
    public String getAppsSummary(ZenMode zenMode) {
        // TODO: b/308819928 - Set summary using priority app list if Selected Apps Chosen.
    public @NonNull String getAppsSummary(@NonNull ZenMode zenMode,
            @Nullable Set<String> appsBypassing) {
        if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) {
            return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
            return formatAppsList(appsBypassing);
        } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
            return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
        } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
@@ -410,4 +416,35 @@ class ZenModeSummaryHelper {
        }
        return "";
    }

    /**
     * Generates a formatted string declaring which apps can interrupt in the style of
     * "App, App2, and 4 more can interrupt."
     * Apps selected for explicit mention are selected in order from the provided set sorted
     * alphabetically.
     */
    public @NonNull String formatAppsList(@Nullable Set<String> appsBypassingDnd) {
        if (appsBypassingDnd == null) {
            return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
        }
        final int numAppsBypassingDnd = appsBypassingDnd.size();
        String[] appsBypassingDndArr = appsBypassingDnd.toArray(new String[numAppsBypassingDnd]);
        // Sorts the provided apps alphabetically.
        Arrays.sort(appsBypassingDndArr);
        MessageFormat msgFormat = new MessageFormat(
                mContext.getString(R.string.zen_mode_apps_subtext),
                Locale.getDefault());
        Map<String, Object> args = new HashMap<>();
        args.put("count", numAppsBypassingDnd);
        if (numAppsBypassingDnd >= 1) {
            args.put("app_1", appsBypassingDndArr[0]);
            if (numAppsBypassingDnd >= 2) {
                args.put("app_2", appsBypassingDndArr[1]);
                if (numAppsBypassingDnd == 3) {
                    args.put("app_3", appsBypassingDndArr[2]);
                }
            }
        }
        return msgFormat.format(args);
    }
}
Loading