Loading res/values/strings.xml +13 −0 Original line number Diff line number Diff line Loading @@ -8043,6 +8043,19 @@ <!-- [CHAR LIMIT=80] Zen mode settings: Allow event notifications/sounds to bypass DND --> <string name="zen_mode_events">Allow events</string> <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND --> <string name="zen_mode_bypassing_apps">Allow apps to override</string> <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND title--> <string name="zen_mode_bypassing_apps_title">Overrides Do Not Disturb</string> <!-- [CHAR LIMIT=80] Zen mode settings: Allow apps to bypass DND --> <plurals name="zen_mode_bypassing_apps_subtext"> <item quantity="zero">No apps can override Do Not Disturb</item> <item quantity="one">1 app can override Do Not Disturb</item> <item quantity="other"><xliff:g id="number" example="2">%1$d</xliff:g> apps can override Do Not Disturb</item> </plurals> <!-- [CHAR LIMIT=50] Zen mode settings: Events (ie: calendar events) --> <string name="zen_mode_events_list">events</string> res/xml/zen_mode_bypassing_apps.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2018 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_mode_bypassing_apps_screen" android:title="@string/zen_mode_bypassing_apps_title" /> res/xml/zen_mode_sound_vibration_settings.xml +50 −45 Original line number Diff line number Diff line Loading @@ -61,7 +61,12 @@ android:key="zen_mode_events" android:title="@string/zen_mode_events"/> <!-- TODO: beverlyt, add "Allow apps to override" --> <!-- Apps overriding DND --> <Preference android:key="zen_mode_bypassing_apps" android:title="@string/zen_mode_bypassing_apps" android:fragment="com.android.settings.notification.ZenModeBypassingAppsSettings"/> </PreferenceCategory> <com.android.settingslib.widget.FooterPreference/> Loading src/com/android/settings/notification/NotificationBackend.java +25 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.app.INotificationManager; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; import android.content.Context; Loading @@ -38,8 +39,6 @@ import android.text.format.DateUtils; import android.util.IconDrawableFactory; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.settingslib.utils.StringUtil; Loading @@ -49,6 +48,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import androidx.annotation.VisibleForTesting; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -208,6 +209,19 @@ public class NotificationBackend { } } /** * Returns all notification channels associated with the package and uid that will bypass DND */ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, int uid) { try { return sINM.getNotificationChannelsBypassingDnd(pkg, uid); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return ParceledListSlice.emptyList(); } } public void updateChannel(String pkg, int uid, NotificationChannel channel) { try { sINM.updateNotificationChannelForPackage(pkg, uid, channel); Loading Loading @@ -260,6 +274,15 @@ public class NotificationBackend { } } public int getNumAppsBypassingDnd(int uid) { try { return sINM.getAppsBypassingDndCount(uid); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return 0; } } public List<NotifyingApp> getRecentApps() { try { return sINM.getRecentNotifyingAppsForUser(UserHandle.myUserId()).getList(); Loading src/com/android/settings/notification/ZenModeAllBypassingAppsPreferenceController.java 0 → 100644 +200 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.notification; import android.app.Application; import android.app.NotificationChannel; import android.content.Context; import android.os.Bundle; import android.provider.Settings; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.widget.apppreference.AppPreference; import java.util.ArrayList; import java.util.List; import androidx.annotation.VisibleForTesting; import androidx.core.text.BidiFormatter; import androidx.fragment.app.Fragment; import androidx.lifecycle.Lifecycle; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; /** * Adds a preference to the PreferenceScreen for each notification channel that can bypass DND. */ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private final String KEY = "zen_mode_bypassing_apps_category"; @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceScreen mPreferenceScreen; @VisibleForTesting Context mPrefContext; private ApplicationsState.Session mAppSession; private NotificationBackend mNotificationBackend = new NotificationBackend(); private Fragment mHostFragment; public ZenModeAllBypassingAppsPreferenceController(Context context, Application app, Fragment host) { this(context, app == null ? null : ApplicationsState.getInstance(app), host); } private ZenModeAllBypassingAppsPreferenceController(Context context, ApplicationsState appState, Fragment host) { super(context); mApplicationsState = appState; mHostFragment = host; if (mApplicationsState != null && host != null) { mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); } } @Override public void displayPreference(PreferenceScreen screen) { mPreferenceScreen = screen; mPrefContext = mPreferenceScreen.getContext(); updateNotificationChannelList(); super.displayPreference(screen); } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY; } /** * Call this method to trigger the notification channels list to refresh. */ public void updateNotificationChannelList() { if (mAppSession == null) { return; } ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED; List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR); if (apps != null) { updateNotificationChannelList(apps); } } @VisibleForTesting void updateNotificationChannelList(List<ApplicationsState.AppEntry> apps) { if (mPreferenceScreen == null || apps == null) { return; } List<Preference> channelsBypassingDnd = new ArrayList<>(); for (ApplicationsState.AppEntry entry : apps) { String pkg = entry.info.packageName; mApplicationsState.ensureIcon(entry); for (NotificationChannel channel : mNotificationBackend .getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList()) { Preference pref = new AppPreference(mPrefContext); pref.setKey(pkg + "|" + channel.getId()); pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label)); pref.setIcon(entry.icon); pref.setSummary(BidiFormatter.getInstance().unicodeWrap(channel.getName())); pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { Bundle args = new Bundle(); args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName); args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid); args.putString(Settings.EXTRA_CHANNEL_ID, channel.getId()); new SubSettingLauncher(mContext) .setDestination(ChannelNotificationSettings.class.getName()) .setArguments(args) .setTitleRes(R.string.notification_channel_title) .setResultListener(mHostFragment, 0) .setSourceMetricsCategory( MetricsEvent.NOTIFICATION_ZEN_MODE_OVERRIDING_APP) .launch(); return true; } }); channelsBypassingDnd.add(pref); } mPreferenceScreen.removeAll(); if (channelsBypassingDnd.size() > 0) { for (Preference prefToAdd : channelsBypassingDnd) { mPreferenceScreen.addPreference(prefToAdd); } } } } private final ApplicationsState.Callbacks mAppSessionCallbacks = new ApplicationsState.Callbacks() { @Override public void onRunningStateChanged(boolean running) { updateNotificationChannelList(); } @Override public void onPackageListChanged() { updateNotificationChannelList(); } @Override public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { updateNotificationChannelList(apps); } @Override public void onPackageIconChanged() { updateNotificationChannelList(); } @Override public void onPackageSizeChanged(String packageName) { updateNotificationChannelList(); } @Override public void onAllSizesComputed() { } @Override public void onLauncherInfoChanged() { updateNotificationChannelList(); } @Override public void onLoadEntriesCompleted() { updateNotificationChannelList(); } }; } Loading
res/values/strings.xml +13 −0 Original line number Diff line number Diff line Loading @@ -8043,6 +8043,19 @@ <!-- [CHAR LIMIT=80] Zen mode settings: Allow event notifications/sounds to bypass DND --> <string name="zen_mode_events">Allow events</string> <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND --> <string name="zen_mode_bypassing_apps">Allow apps to override</string> <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND title--> <string name="zen_mode_bypassing_apps_title">Overrides Do Not Disturb</string> <!-- [CHAR LIMIT=80] Zen mode settings: Allow apps to bypass DND --> <plurals name="zen_mode_bypassing_apps_subtext"> <item quantity="zero">No apps can override Do Not Disturb</item> <item quantity="one">1 app can override Do Not Disturb</item> <item quantity="other"><xliff:g id="number" example="2">%1$d</xliff:g> apps can override Do Not Disturb</item> </plurals> <!-- [CHAR LIMIT=50] Zen mode settings: Events (ie: calendar events) --> <string name="zen_mode_events_list">events</string>
res/xml/zen_mode_bypassing_apps.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2018 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_mode_bypassing_apps_screen" android:title="@string/zen_mode_bypassing_apps_title" />
res/xml/zen_mode_sound_vibration_settings.xml +50 −45 Original line number Diff line number Diff line Loading @@ -61,7 +61,12 @@ android:key="zen_mode_events" android:title="@string/zen_mode_events"/> <!-- TODO: beverlyt, add "Allow apps to override" --> <!-- Apps overriding DND --> <Preference android:key="zen_mode_bypassing_apps" android:title="@string/zen_mode_bypassing_apps" android:fragment="com.android.settings.notification.ZenModeBypassingAppsSettings"/> </PreferenceCategory> <com.android.settingslib.widget.FooterPreference/> Loading
src/com/android/settings/notification/NotificationBackend.java +25 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.app.INotificationManager; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; import android.content.Context; Loading @@ -38,8 +39,6 @@ import android.text.format.DateUtils; import android.util.IconDrawableFactory; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.Utils; import com.android.settingslib.utils.StringUtil; Loading @@ -49,6 +48,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import androidx.annotation.VisibleForTesting; public class NotificationBackend { private static final String TAG = "NotificationBackend"; Loading Loading @@ -208,6 +209,19 @@ public class NotificationBackend { } } /** * Returns all notification channels associated with the package and uid that will bypass DND */ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, int uid) { try { return sINM.getNotificationChannelsBypassingDnd(pkg, uid); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return ParceledListSlice.emptyList(); } } public void updateChannel(String pkg, int uid, NotificationChannel channel) { try { sINM.updateNotificationChannelForPackage(pkg, uid, channel); Loading Loading @@ -260,6 +274,15 @@ public class NotificationBackend { } } public int getNumAppsBypassingDnd(int uid) { try { return sINM.getAppsBypassingDndCount(uid); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); return 0; } } public List<NotifyingApp> getRecentApps() { try { return sINM.getRecentNotifyingAppsForUser(UserHandle.myUserId()).getList(); Loading
src/com/android/settings/notification/ZenModeAllBypassingAppsPreferenceController.java 0 → 100644 +200 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.notification; import android.app.Application; import android.app.NotificationChannel; import android.content.Context; import android.os.Bundle; import android.provider.Settings; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.widget.apppreference.AppPreference; import java.util.ArrayList; import java.util.List; import androidx.annotation.VisibleForTesting; import androidx.core.text.BidiFormatter; import androidx.fragment.app.Fragment; import androidx.lifecycle.Lifecycle; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; /** * Adds a preference to the PreferenceScreen for each notification channel that can bypass DND. */ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { private final String KEY = "zen_mode_bypassing_apps_category"; @VisibleForTesting ApplicationsState mApplicationsState; @VisibleForTesting PreferenceScreen mPreferenceScreen; @VisibleForTesting Context mPrefContext; private ApplicationsState.Session mAppSession; private NotificationBackend mNotificationBackend = new NotificationBackend(); private Fragment mHostFragment; public ZenModeAllBypassingAppsPreferenceController(Context context, Application app, Fragment host) { this(context, app == null ? null : ApplicationsState.getInstance(app), host); } private ZenModeAllBypassingAppsPreferenceController(Context context, ApplicationsState appState, Fragment host) { super(context); mApplicationsState = appState; mHostFragment = host; if (mApplicationsState != null && host != null) { mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); } } @Override public void displayPreference(PreferenceScreen screen) { mPreferenceScreen = screen; mPrefContext = mPreferenceScreen.getContext(); updateNotificationChannelList(); super.displayPreference(screen); } @Override public boolean isAvailable() { return true; } @Override public String getPreferenceKey() { return KEY; } /** * Call this method to trigger the notification channels list to refresh. */ public void updateNotificationChannelList() { if (mAppSession == null) { return; } ApplicationsState.AppFilter filter = ApplicationsState.FILTER_ALL_ENABLED; List<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR); if (apps != null) { updateNotificationChannelList(apps); } } @VisibleForTesting void updateNotificationChannelList(List<ApplicationsState.AppEntry> apps) { if (mPreferenceScreen == null || apps == null) { return; } List<Preference> channelsBypassingDnd = new ArrayList<>(); for (ApplicationsState.AppEntry entry : apps) { String pkg = entry.info.packageName; mApplicationsState.ensureIcon(entry); for (NotificationChannel channel : mNotificationBackend .getNotificationChannelsBypassingDnd(pkg, entry.info.uid).getList()) { Preference pref = new AppPreference(mPrefContext); pref.setKey(pkg + "|" + channel.getId()); pref.setTitle(BidiFormatter.getInstance().unicodeWrap(entry.label)); pref.setIcon(entry.icon); pref.setSummary(BidiFormatter.getInstance().unicodeWrap(channel.getName())); pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { Bundle args = new Bundle(); args.putString(AppInfoBase.ARG_PACKAGE_NAME, entry.info.packageName); args.putInt(AppInfoBase.ARG_PACKAGE_UID, entry.info.uid); args.putString(Settings.EXTRA_CHANNEL_ID, channel.getId()); new SubSettingLauncher(mContext) .setDestination(ChannelNotificationSettings.class.getName()) .setArguments(args) .setTitleRes(R.string.notification_channel_title) .setResultListener(mHostFragment, 0) .setSourceMetricsCategory( MetricsEvent.NOTIFICATION_ZEN_MODE_OVERRIDING_APP) .launch(); return true; } }); channelsBypassingDnd.add(pref); } mPreferenceScreen.removeAll(); if (channelsBypassingDnd.size() > 0) { for (Preference prefToAdd : channelsBypassingDnd) { mPreferenceScreen.addPreference(prefToAdd); } } } } private final ApplicationsState.Callbacks mAppSessionCallbacks = new ApplicationsState.Callbacks() { @Override public void onRunningStateChanged(boolean running) { updateNotificationChannelList(); } @Override public void onPackageListChanged() { updateNotificationChannelList(); } @Override public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { updateNotificationChannelList(apps); } @Override public void onPackageIconChanged() { updateNotificationChannelList(); } @Override public void onPackageSizeChanged(String packageName) { updateNotificationChannelList(); } @Override public void onAllSizesComputed() { } @Override public void onLauncherInfoChanged() { updateNotificationChannelList(); } @Override public void onLoadEntriesCompleted() { updateNotificationChannelList(); } }; }