Merge changes from topic "b308819928-ModesSettingsAppBreakthroughPage" into main

* changes:
  Modify Summary for Mode's Apps settings page
  Adds summary helper for apps subtitle
This commit is contained in:
Alexander Roederer
2024-06-16 02:49:53 +00:00
committed by Android (Google) Code Review
7 changed files with 362 additions and 24 deletions

View File

@@ -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);

View File

@@ -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();
}
};
}

View File

@@ -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(

View File

@@ -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);
}
}