From 8e032e15e6b1b6a2f44fd3d5063ee83f45d71189 Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Wed, 25 Jan 2017 13:41:38 -0500 Subject: [PATCH] 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 --- .../plugins/PluginInstanceManager.java | 5 +- packages/SystemUI/res/values/strings.xml | 11 +++ .../android/systemui/SystemUIApplication.java | 2 + .../power/PowerNotificationWarnings.java | 8 +-- .../systemui/screenshot/GlobalScreenshot.java | 5 ++ .../systemui/statusbar/BaseStatusBar.java | 3 +- .../policy/UserSwitcherController.java | 5 +- .../systemui/usb/StorageNotification.java | 14 ++-- .../systemui/util/NotificationChannels.java | 65 +++++++++++++++++ .../android/systemui/util/ChannelsTest.java | 69 +++++++++++++++++++ 10 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java 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 9f44bd4bc3311..d71b6bdafe199 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 @@ public class PluginInstanceManager { 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 mListener; private final String mAction; @@ -312,7 +315,7 @@ public class PluginInstanceManager { .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 ab27d07753512..7f4baa526650a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1800,4 +1800,15 @@ Prompt for password + + Alerts + + Screenshots + + Security + + User status + + Storage + diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index f2aaec1dcfad6..afe88c1cd6c2b 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.SystemBars; 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 @@ public class SystemUIApplication extends Application implements SysUiServiceProv 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 2fe9e77b4c168..94ea4dcf920c4 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.R; 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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .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 db021ffee9b64..7135cafc6bdf2 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 android.widget.ImageView; 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 @@ class SaveImageInBackgroundTask extends AsyncTask { // 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 @@ class SaveImageInBackgroundTask extends AsyncTask { 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 @@ class SaveImageInBackgroundTask extends AsyncTask { // 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 @@ class SaveImageInBackgroundTask extends AsyncTask { .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 f0de6961658d2..340b6038b39bd 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.PreviewInflater; 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 @@ public abstract class BaseStatusBar extends SystemUI implements .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 f71c5d1dc41c8..fd71f43ceacfc 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.plugins.qs.QS.DetailAdapter; 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 @@ public class UserSwitcherController { 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 @@ public class UserSwitcherController { 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 2c90e62d3a1bc..9a16d6db4bedc 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 android.util.SparseArray; 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 @@ public class StorageNotification extends SystemUI { .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 @@ public class StorageNotification extends SystemUI { 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 @@ public class StorageNotification extends SystemUI { return buildNotificationBuilder(vol, title, text) .setCategory(Notification.CATEGORY_PROGRESS) - .setPriority(Notification.PRIORITY_LOW) .setOngoing(true) .build(); } @@ -360,7 +362,6 @@ public class StorageNotification extends SystemUI { buildUnmountPendingIntent(vol))) .setContentIntent(initIntent) .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid())) - .setCategory(Notification.CATEGORY_SYSTEM) .build(); } else { @@ -377,8 +378,7 @@ public class StorageNotification extends SystemUI { 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 @@ public class StorageNotification extends SystemUI { return buildNotificationBuilder(vol, title, text) .setCategory(Notification.CATEGORY_PROGRESS) - .setPriority(Notification.PRIORITY_LOW) .setOngoing(true) .build(); } @@ -485,8 +484,8 @@ public class StorageNotification extends SystemUI { .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 @@ public class StorageNotification extends SystemUI { .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 @@ public class StorageNotification extends SystemUI { 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 0000000000000..6bb8aea6c95e2 --- /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 0000000000000..8949598b029ce --- /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 ALL_CHANNELS = new ArraySet<>(Arrays.asList( + NotificationChannels.ALERTS, + NotificationChannels.SCREENSHOTS, + NotificationChannels.SECURITY, + NotificationChannels.USER, + NotificationChannels.STORAGE + )); + NotificationChannels.createAll(mContext); + ArgumentCaptor captor = ArgumentCaptor.forClass(List.class); + verify(mMockNotificationManager).createNotificationChannels(captor.capture()); + final List list = captor.getValue(); + assertEquals(ALL_CHANNELS.size(), list.size()); + list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId()))); + } +}