• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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