Notification Channels come to SystemUI's own notifications.
There are five channels at present:
ALERTS - low battery etc.
SCREENSHOTS - progress & result
SECURITY - notifications hidden due to policy
STORAGE - disk low
STATUS - basically everything else
The importance for each channel should match the legacy
priority of the notifications it carries.
Bug: 34250937
Test: runtest systemui
Change-Id: I5915ca453258caea63b0d9bd756893db05e8d600
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
index 9f44bd4..d71b6bd 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -51,6 +51,9 @@
private static final String TAG = "PluginInstanceManager";
private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
+ // must be one of the channels created in NotificationChannels.java
+ private static final String NOTIFICATION_CHANNEL_ID = "ALR";
+
private final Context mContext;
private final PluginListener<T> mListener;
private final String mAction;
@@ -312,7 +315,7 @@
.setSmallIcon(icon)
.setWhen(0)
.setShowWhen(false)
- .setPriority(Notification.PRIORITY_MAX)
+ .setChannel(NOTIFICATION_CHANNEL_ID)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setColor(mContext.getColor(color));
String label = cls;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ab27d07..7f4baa5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1800,4 +1800,15 @@
<!-- SysUI Tuner: Switch to control if device gets unlocked [CHAR LIMIT=60] -->
<string name="lockscreen_unlock">Prompt for password</string>
+ <!-- Title for the notification channel containing important alerts like low battery. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_alerts">Alerts</string>
+ <!-- Title for the notification channel dedicated to screenshot progress. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_screenshot">Screenshots</string>
+ <!-- Title for the notification channel for urgent security issues. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_security">Security</string>
+ <!-- Title for the notification channel containing multi-user status information. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_user_status">User status</string>
+ <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_storage">Storage</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index f2aaec1..afe88c1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.usb.StorageNotification;
+import com.android.systemui.util.NotificationChannels;
import com.android.systemui.volume.VolumeUI;
import java.util.HashMap;
@@ -66,6 +67,7 @@
Dependency.class,
FragmentService.class,
TunerService.class,
+ NotificationChannels.class,
CommandQueue.CommandQueueStart.class,
KeyguardViewMediator.class,
Recents.class,
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 2fe9e77..94ea4dc 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -43,6 +43,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.NotificationChannels;
import java.io.PrintWriter;
import java.text.NumberFormat;
@@ -151,8 +152,7 @@
.setOngoing(true)
.setContentTitle(mContext.getString(R.string.invalid_charger_title))
.setContentText(mContext.getString(R.string.invalid_charger_text))
- .setPriority(Notification.PRIORITY_MAX)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setChannel(NotificationChannels.ALERTS)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color));
SystemUI.overrideNotificationAppName(mContext, nb);
@@ -173,7 +173,7 @@
.setContentText(mContext.getString(textRes, percentage))
.setOnlyAlertOnce(true)
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
- .setPriority(Notification.PRIORITY_MAX)
+ .setChannel(NotificationChannels.ALERTS)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setColor(mContext.getColor(
com.android.internal.R.color.battery_saver_mode_color));
@@ -241,7 +241,7 @@
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.high_temp_title))
.setContentText(mContext.getString(R.string.high_temp_notif_message))
- .setPriority(Notification.PRIORITY_HIGH)
+ .setChannel(NotificationChannels.ALERTS)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index db021ff..7135caf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -62,6 +62,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.util.NotificationChannels;
import java.io.File;
import java.io.FileOutputStream;
@@ -178,6 +179,7 @@
// The public notification will show similar info but with the actual screenshot omitted
mPublicNotificationBuilder = new Notification.Builder(context)
+ .setChannel(NotificationChannels.SCREENSHOTS)
.setContentTitle(r.getString(R.string.screenshot_saving_title))
.setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
@@ -189,6 +191,7 @@
SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder);
mNotificationBuilder = new Notification.Builder(context)
+ .setChannel(NotificationChannels.SCREENSHOTS)
.setTicker(r.getString(R.string.screenshot_saving_ticker)
+ (mTickerAddSpace ? " " : ""))
.setContentTitle(r.getString(R.string.screenshot_saving_title))
@@ -332,6 +335,7 @@
// Update the text and the icon for the existing notification
mPublicNotificationBuilder
+ .setChannel(NotificationChannels.SCREENSHOTS)
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
@@ -340,6 +344,7 @@
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
mNotificationBuilder
+ .setChannel(NotificationChannels.SCREENSHOTS)
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f0de696..340b603 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -117,6 +117,7 @@
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.util.NotificationChannels;
import java.util.ArrayList;
import java.util.Collections;
@@ -882,7 +883,7 @@
.setSmallIcon(R.drawable.ic_android)
.setContentTitle(mContext.getString(R.string.hidden_notifications_title))
.setContentText(mContext.getString(R.string.hidden_notifications_text))
- .setPriority(Notification.PRIORITY_HIGH)
+ .setChannel(NotificationChannels.SECURITY)
.setOngoing(true)
.setColor(mContext.getColor(colorRes))
.setContentIntent(setupIntent)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index f71c5d1..fd71f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -61,6 +61,7 @@
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.NotificationChannels;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -561,7 +562,7 @@
0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
Notification.Builder builder = new Notification.Builder(mContext)
.setVisibility(Notification.VISIBILITY_SECRET)
- .setPriority(Notification.PRIORITY_MIN)
+ .setChannel(NotificationChannels.USER)
.setSmallIcon(R.drawable.ic_person)
.setContentTitle(mContext.getString(R.string.user_logout_notification_title))
.setContentText(mContext.getString(R.string.user_logout_notification_text))
@@ -585,7 +586,7 @@
Notification.Builder builder = new Notification.Builder(mContext)
.setVisibility(Notification.VISIBILITY_SECRET)
- .setPriority(Notification.PRIORITY_MIN)
+ .setChannel(NotificationChannels.USER)
.setSmallIcon(R.drawable.ic_person)
.setContentTitle(mContext.getString(R.string.guest_notification_title))
.setContentText(mContext.getString(R.string.guest_notification_text))
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 2c90e62..9a16d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -43,6 +43,7 @@
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.SystemUI;
+import com.android.systemui.util.NotificationChannels;
import java.util.List;
@@ -206,6 +207,7 @@
.setStyle(new Notification.BigTextStyle().bigText(text))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setLocalOnly(true)
+ .setChannel(NotificationChannels.STORAGE)
.setCategory(Notification.CATEGORY_SYSTEM)
.setDeleteIntent(buildSnoozeIntent(fsUuid));
SystemUI.overrideNotificationAppName(mContext, builder);
@@ -225,6 +227,7 @@
R.string.ext_media_unsupported_notification_message, disk.getDescription());
Notification.Builder builder = new Notification.Builder(mContext)
+ .setChannel(NotificationChannels.STORAGE)
.setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE))
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setContentTitle(title)
@@ -331,7 +334,6 @@
return buildNotificationBuilder(vol, title, text)
.setCategory(Notification.CATEGORY_PROGRESS)
- .setPriority(Notification.PRIORITY_LOW)
.setOngoing(true)
.build();
}
@@ -360,7 +362,6 @@
buildUnmountPendingIntent(vol)))
.setContentIntent(initIntent)
.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
- .setCategory(Notification.CATEGORY_SYSTEM)
.build();
} else {
@@ -377,8 +378,7 @@
mContext.getString(R.string.ext_media_unmount_action),
buildUnmountPendingIntent(vol)))
.setContentIntent(browseIntent)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setPriority(Notification.PRIORITY_LOW);
+ .setCategory(Notification.CATEGORY_SYSTEM);
// Non-adoptable disks can't be snoozed.
if (disk.isAdoptable()) {
builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()));
@@ -402,7 +402,6 @@
return buildNotificationBuilder(vol, title, text)
.setCategory(Notification.CATEGORY_PROGRESS)
- .setPriority(Notification.PRIORITY_LOW)
.setOngoing(true)
.build();
}
@@ -485,8 +484,8 @@
.setStyle(new Notification.BigTextStyle().bigText(text))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setLocalOnly(true)
+ .setChannel(NotificationChannels.STORAGE)
.setCategory(Notification.CATEGORY_PROGRESS)
- .setPriority(Notification.PRIORITY_LOW)
.setProgress(100, status, false)
.setOngoing(true);
SystemUI.overrideNotificationAppName(mContext, builder);
@@ -537,7 +536,7 @@
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setLocalOnly(true)
.setCategory(Notification.CATEGORY_SYSTEM)
- .setPriority(Notification.PRIORITY_LOW)
+ .setChannel(NotificationChannels.STORAGE)
.setAutoCancel(true);
SystemUI.overrideNotificationAppName(mContext, builder);
@@ -564,6 +563,7 @@
private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
CharSequence text) {
Notification.Builder builder = new Notification.Builder(mContext)
+ .setChannel(NotificationChannels.STORAGE)
.setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState()))
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setContentTitle(title)
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
new file mode 100644
index 0000000..6bb8aea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.systemui.util;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+
+import android.content.Context;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import java.util.Arrays;
+
+public class NotificationChannels extends SystemUI {
+ public static String ALERTS = "ALR";
+ public static String SCREENSHOTS = "SCN";
+ public static String SECURITY = "SEC";
+ public static String USER = "USR";
+ public static String STORAGE = "DSK";
+
+ @VisibleForTesting
+ static void createAll(Context context) {
+ final NotificationManager nm = context.getSystemService(NotificationManager.class);
+ nm.createNotificationChannels(Arrays.asList(
+ new NotificationChannel(
+ ALERTS,
+ context.getString(R.string.notification_channel_alerts),
+ NotificationManager.IMPORTANCE_HIGH),
+ new NotificationChannel(
+ SCREENSHOTS,
+ context.getString(R.string.notification_channel_screenshot),
+ NotificationManager.IMPORTANCE_DEFAULT),
+ new NotificationChannel(
+ SECURITY,
+ context.getString(R.string.notification_channel_security),
+ NotificationManager.IMPORTANCE_HIGH),
+ new NotificationChannel(
+ USER,
+ context.getString(R.string.notification_channel_user_status),
+ NotificationManager.IMPORTANCE_MIN),
+ new NotificationChannel(
+ STORAGE,
+ context.getString(R.string.notification_channel_storage),
+ NotificationManager.IMPORTANCE_LOW)
+ ));
+ }
+
+ @Override
+ public void start() {
+ createAll(mContext);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
new file mode 100644
index 0000000..8949598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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.systemui.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.NotificationChannels;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ChannelsTest extends SysuiTestCase {
+ private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+
+ @Before
+ public void setup() throws Exception {
+ mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager);
+ }
+
+ @Test
+ public void testChannelSetup() {
+ Set<String> ALL_CHANNELS = new ArraySet<>(Arrays.asList(
+ NotificationChannels.ALERTS,
+ NotificationChannels.SCREENSHOTS,
+ NotificationChannels.SECURITY,
+ NotificationChannels.USER,
+ NotificationChannels.STORAGE
+ ));
+ NotificationChannels.createAll(mContext);
+ ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+ verify(mMockNotificationManager).createNotificationChannels(captor.capture());
+ final List<NotificationChannel> list = captor.getValue();
+ assertEquals(ALL_CHANNELS.size(), list.size());
+ list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId())));
+ }
+}