1 package com.android.settings.notification.zen; 2 3 import android.app.Application; 4 import android.app.NotificationChannel; 5 import android.content.Context; 6 import android.icu.text.MessageFormat; 7 import android.provider.Settings; 8 import android.text.TextUtils; 9 import android.util.ArraySet; 10 11 import androidx.core.text.BidiFormatter; 12 import androidx.fragment.app.Fragment; 13 import androidx.preference.Preference; 14 import androidx.preference.PreferenceScreen; 15 16 import com.android.internal.annotations.VisibleForTesting; 17 import com.android.settings.R; 18 import com.android.settings.core.PreferenceControllerMixin; 19 import com.android.settings.notification.NotificationBackend; 20 import com.android.settingslib.applications.ApplicationsState; 21 import com.android.settingslib.core.lifecycle.Lifecycle; 22 23 import java.util.ArrayList; 24 import java.util.HashMap; 25 import java.util.List; 26 import java.util.Locale; 27 import java.util.Map; 28 import java.util.Set; 29 30 /** 31 * Controls the summary for preference found at: 32 * Settings > Sound > Do Not Disturb > Apps 33 */ 34 public class ZenModeBypassingAppsPreferenceController extends AbstractZenModePreferenceController 35 implements PreferenceControllerMixin { 36 37 protected static final String KEY = "zen_mode_behavior_apps"; 38 39 @VisibleForTesting protected Preference mPreference; 40 private ApplicationsState.Session mAppSession; 41 private NotificationBackend mNotificationBackend = new NotificationBackend(); 42 43 private String mSummary; 44 ZenModeBypassingAppsPreferenceController(Context context, Application app, Fragment host, Lifecycle lifecycle)45 public ZenModeBypassingAppsPreferenceController(Context context, Application app, 46 Fragment host, Lifecycle lifecycle) { 47 this(context, app == null ? null : ApplicationsState.getInstance(app), host, lifecycle); 48 } 49 ZenModeBypassingAppsPreferenceController(Context context, ApplicationsState appState, Fragment host, Lifecycle lifecycle)50 private ZenModeBypassingAppsPreferenceController(Context context, ApplicationsState appState, 51 Fragment host, Lifecycle lifecycle) { 52 super(context, KEY, lifecycle); 53 if (appState != null && host != null) { 54 mAppSession = appState.newSession(mAppSessionCallbacks, host.getLifecycle()); 55 } 56 } 57 58 @Override isAvailable()59 public boolean isAvailable() { 60 return true; 61 } 62 63 @Override getPreferenceKey()64 public String getPreferenceKey() { 65 return KEY; 66 } 67 68 @Override displayPreference(PreferenceScreen screen)69 public void displayPreference(PreferenceScreen screen) { 70 mPreference = screen.findPreference(KEY); 71 updateAppsBypassingDndSummaryText(); 72 super.displayPreference(screen); 73 } 74 75 @Override getSummary()76 public String getSummary() { 77 return mSummary; 78 } 79 updateAppsBypassingDndSummaryText()80 private void updateAppsBypassingDndSummaryText() { 81 if (mAppSession == null) { 82 return; 83 } 84 85 ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures() 86 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() 87 ? ApplicationsState.FILTER_ENABLED_NOT_QUIET 88 : ApplicationsState.FILTER_ALL_ENABLED; 89 List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, 90 ApplicationsState.ALPHA_COMPARATOR); 91 updateAppsBypassingDndSummaryText(apps); 92 } 93 94 @VisibleForTesting updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps)95 void updateAppsBypassingDndSummaryText(List<ApplicationsState.AppEntry> apps) { 96 switch (getZenMode()) { 97 case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS: 98 case Settings.Global.ZEN_MODE_ALARMS: 99 // users cannot change their DND settings when an app puts the device total 100 // silence or alarms only (both deprecated) modes 101 mPreference.setEnabled(false); 102 mSummary = mContext.getResources().getString( 103 R.string.zen_mode_bypassing_apps_subtext_none); 104 return; 105 default: 106 mPreference.setEnabled(true); 107 } 108 109 if (apps == null) { 110 return; 111 } 112 113 Set<String> appsBypassingDnd = new ArraySet<>(); 114 for (ApplicationsState.AppEntry entry : apps) { 115 String pkg = entry.info.packageName; 116 for (NotificationChannel channel : mNotificationBackend 117 .getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList()) { 118 if (!TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) { 119 // conversation channels that bypass dnd will be shown on the People page 120 continue; 121 } 122 appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(entry.label)); 123 continue; 124 } 125 } 126 127 final int numAppsBypassingDnd = appsBypassingDnd.size(); 128 String[] appsBypassingDndArr = appsBypassingDnd.toArray(new String[numAppsBypassingDnd]); 129 MessageFormat msgFormat = new MessageFormat( 130 mContext.getString(R.string.zen_mode_bypassing_apps_subtext), 131 Locale.getDefault()); 132 Map<String, Object> args = new HashMap<>(); 133 args.put("count", numAppsBypassingDnd); 134 if (numAppsBypassingDnd >= 1) { 135 args.put("app_1", appsBypassingDndArr[0]); 136 if (numAppsBypassingDnd >= 2) { 137 args.put("app_2", appsBypassingDndArr[1]); 138 if (numAppsBypassingDnd == 3) { 139 args.put("app_3", appsBypassingDndArr[2]); 140 } 141 } 142 } 143 144 mSummary = msgFormat.format(args); 145 refreshSummary(mPreference); 146 } 147 148 private final ApplicationsState.Callbacks mAppSessionCallbacks = 149 new ApplicationsState.Callbacks() { 150 151 @Override 152 public void onRunningStateChanged(boolean running) { 153 updateAppsBypassingDndSummaryText(); 154 } 155 156 @Override 157 public void onPackageListChanged() { 158 updateAppsBypassingDndSummaryText(); 159 } 160 161 @Override 162 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { 163 updateAppsBypassingDndSummaryText(apps); 164 } 165 166 @Override 167 public void onPackageIconChanged() { } 168 169 @Override 170 public void onPackageSizeChanged(String packageName) { 171 updateAppsBypassingDndSummaryText(); 172 } 173 174 @Override 175 public void onAllSizesComputed() { } 176 177 @Override 178 public void onLauncherInfoChanged() { } 179 180 @Override 181 public void onLoadEntriesCompleted() { 182 updateAppsBypassingDndSummaryText(); 183 } 184 }; 185 } 186