Create notification channel interstitial shelf
From NotificationGuts, tapping "Turn off notifications" will now present
a half-shelf allowing the user to directly block up to 4 channels (the
one given from the notification guts + up to 3 other channels from
that app) or the app itself.
Test: visual (for now)
Bug: 130307442
Fixes: 131432719
Change-Id: I7e82928dfd56b9e25e5bef02607eede55b11d9e3
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
new file mode 100644
index 0000000..a563bb5
--- /dev/null
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -0,0 +1,126 @@
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/half_shelf_dialog"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+>
+
+ <LinearLayout
+ android:id="@+id/half_shelf"
+ android:layout_width="@dimen/qs_panel_width"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@drawable/rounded_bg_full" >
+
+ <com.android.systemui.statusbar.notification.row.ChannelEditorListView
+ android:id="@+id/half_shelf_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="bottom"
+ android:orientation="vertical" >
+
+ <com.android.systemui.statusbar.notification.row.AppControlView
+ android:id="@+id/app_control"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:padding="8dp" />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:padding="8dp"
+ android:gravity="center_vertical|start"
+ android:textSize="15sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/TextAppearance.NotificationInfo.Title" />
+
+ <Switch
+ android:id="@+id/toggle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="8dp" />
+ </com.android.systemui.statusbar.notification.row.AppControlView>
+ <!-- divider view -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/notification_channel_dialog_separator"
+ />
+
+ <!-- ChannelRows get added dynamically -->
+
+ </com.android.systemui.statusbar.notification.row.ChannelEditorListView>
+ <!-- divider view -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/notification_channel_dialog_separator"
+ />
+ <RelativeLayout
+ android:id="@+id/bottom_actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/notification_guts_button_spacing"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp" >
+ <TextView
+ android:id="@+id/see_more_button"
+ android:text="@string/see_more_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/done_button"
+ android:text="@string/inline_ok_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:gravity="end|center_vertical"
+ android:maxWidth="125dp"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:layout_alignParentEnd="true"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
new file mode 100644
index 0000000..17ea931
--- /dev/null
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -0,0 +1,78 @@
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+
+<com.android.systemui.statusbar.notification.row.ChannelRow
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/half_shelf_row"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:layout_gravity="center_vertical"
+ android:padding="8dp"
+ />
+
+ <RelativeLayout
+ android:id="@+id/description_container"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:gravity="left|center_vertical"
+ android:orientation="vertical"
+ >
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingBottom="0dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:gravity="center_vertical|start"
+ android:textSize="14sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/TextAppearance.NotificationInfo.Title"
+ />
+
+ <TextView
+ android:id="@+id/channel_description"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:gravity="center_vertical|start"
+ android:textSize="14sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_below="@id/channel_name"
+ style="@style/TextAppearance.NotificationInfo.Secondary"
+ />
+ </RelativeLayout>
+
+ <Switch
+ android:id="@+id/toggle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="8dp"
+ />
+</com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index a538558..e25faa2 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -50,6 +50,7 @@
<color name="notification_guts_button_color">@color/GM2_blue_200</color>
<color name="notification_section_header_label_color">@color/GM2_grey_200</color>
+ <color name="notification_channel_dialog_separator">@color/GM2_grey_700</color>
<!-- The color of the background in the top part of QSCustomizer -->
<color name="qs_customize_background">@color/GM2_grey_900</color>
@@ -74,4 +75,4 @@
<color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal -->
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 14a120b..6d7e205 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -98,6 +98,8 @@
<color name="notification_guts_button_color">@color/GM2_blue_700</color>
<color name="notification_section_header_label_color">@color/GM2_grey_900</color>
+ <!-- The divider view for the notification channel editor half-shelf -->
+ <color name="notification_channel_dialog_separator">@color/GM2_grey_200</color>
<color name="assist_orb_color">#ffffff</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ba72b6..556ce7a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1688,6 +1688,12 @@
<!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
<string name="notification_delegate_header">Proxied notification</string>
+ <!-- [CHAR LIMIT=40 Notification: Label for the inline channel blocking view -->
+ <string name="notification_channel_dialog_title">All <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> notifications</string>
+
+ <!-- [CHAR LIMIT=20 Notification: "See more" button -->
+ <string name="see_more_title">See more</string>
+
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
<string name="appops_camera">This app is using the camera.</string>
<!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 083418e..16328f8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -450,6 +450,12 @@
<item name="android:alpha">0.54</item>
</style>
+ <style name="TextAppearance.NotificationInfo.Title">
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:textColor">@color/notification_primary_text_color</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
<item name="android:textSize">16sp</item>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 4b6306a..48a7495 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,6 +15,7 @@
package com.android.systemui;
import android.annotation.Nullable;
+import android.app.INotificationManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
@@ -68,6 +69,7 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -298,6 +300,8 @@
@Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
@Inject Lazy<DumpController> mDumpController;
@Inject Lazy<DockManager> mDockManager;
+ @Inject Lazy<ChannelEditorDialogController> mChannelEditorDialogController;
+ @Inject Lazy<INotificationManager> mINotificationManager;
@Inject
public Dependency() {
@@ -473,6 +477,8 @@
mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
mProviders.put(DumpController.class, mDumpController::get);
mProviders.put(DockManager.class, mDockManager::get);
+ mProviders.put(ChannelEditorDialogController.class, mChannelEditorDialogController::get);
+ mProviders.put(INotificationManager.class, mINotificationManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index f649976..321206f 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -22,6 +22,7 @@
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.annotation.Nullable;
+import android.app.INotificationManager;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.NightDisplayListener;
@@ -132,6 +133,14 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
+ /** */
+ @Singleton
+ @Provides
+ public INotificationManager provideINotificationManager() {
+ return INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+
@Singleton
@Provides
// Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
new file mode 100644
index 0000000..a065f67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2019 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.statusbar.notification.row
+
+import android.app.Dialog
+import android.app.INotificationManager
+import android.app.NotificationChannel
+import android.app.NotificationChannel.DEFAULT_CHANNEL_ID
+import android.app.NotificationChannelGroup
+import android.app.NotificationManager.IMPORTANCE_NONE
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ColorDrawable
+import android.util.Log
+import android.view.Gravity
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.Window
+import android.view.WindowManager
+import android.widget.TextView
+import com.android.internal.annotations.VisibleForTesting
+
+import com.android.systemui.R
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+const val TAG = "ChannelDialogController"
+
+@Singleton
+class ChannelEditorDialogController @Inject constructor(
+ c: Context,
+ private val noMan: INotificationManager
+) {
+ val context: Context = c.applicationContext
+
+ lateinit var dialog: Dialog
+
+ private var appIcon: Drawable? = null
+ private var appUid: Int? = null
+ private var packageName: String? = null
+ private var appName: String? = null
+ private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
+
+ // Channels handed to us from NotificationInfo
+ @VisibleForTesting
+ internal val providedChannels = mutableListOf<NotificationChannel>()
+
+ // Map from NotificationChannel to importance
+ private val edits = mutableMapOf<NotificationChannel, Int>()
+ var appNotificationsEnabled = true
+
+ // Keep a mapping of NotificationChannel.getGroup() to the actual group name for display
+ @VisibleForTesting
+ internal val groupNameLookup = hashMapOf<String, CharSequence>()
+ private val channelGroupList = mutableListOf<NotificationChannelGroup>()
+
+ fun prepareDialogForApp(
+ appName: String,
+ packageName: String,
+ uid: Int,
+ channels: Set<NotificationChannel>,
+ appIcon: Drawable,
+ onSettingsClickListener: NotificationInfo.OnSettingsClickListener
+ ) {
+ this.appName = appName
+ this.packageName = packageName
+ this.appUid = uid
+ this.appIcon = appIcon
+ this.appNotificationsEnabled = checkAreAppNotificationsOn()
+ this.onSettingsClickListener = onSettingsClickListener
+
+ channelGroupList.clear()
+ channelGroupList.addAll(fetchNotificationChannelGroups())
+ buildGroupNameLookup()
+ padToFourChannels(channels)
+ }
+
+ private fun buildGroupNameLookup() {
+ channelGroupList.forEach { group ->
+ if (group.id != null) {
+ groupNameLookup[group.id] = group.name
+ }
+ }
+ }
+
+ private fun padToFourChannels(channels: Set<NotificationChannel>) {
+ providedChannels.clear()
+ // First, add all of the given channels
+ providedChannels.addAll(channels.asSequence().take(4))
+
+ // Then pad to 4 if we haven't been given that many
+ providedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence())
+ .filterNot { providedChannels.contains(it) }
+ .distinct()
+ .take(4 - providedChannels.size))
+
+ // If we only got one channel and it has the default miscellaneous tag, then we actually
+ // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the
+ // channel
+ if (providedChannels.size == 1 && DEFAULT_CHANNEL_ID == providedChannels[0].id) {
+ providedChannels.clear()
+ }
+ }
+
+ private fun getDisplayableChannels(
+ groupList: Sequence<NotificationChannelGroup>
+ ): Sequence<NotificationChannel> {
+
+ val channels = groupList
+ .flatMap { group ->
+ group.channels.asSequence().filterNot { channel ->
+ channel.isImportanceLockedByOEM
+ || channel.importance == IMPORTANCE_NONE
+ || channel.isImportanceLockedByCriticalDeviceFunction
+ }
+ }
+
+ // TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not)
+ return channels.sortedWith(compareBy { it.name?.toString() ?: it.id })
+ }
+
+ fun show() {
+ initDialog()
+ dialog.show()
+ }
+
+ private fun done() {
+ resetState()
+ dialog.dismiss()
+ }
+
+ private fun resetState() {
+ appIcon = null
+ appUid = null
+ packageName = null
+ appName = null
+
+ edits.clear()
+ providedChannels.clear()
+ groupNameLookup.clear()
+ }
+
+ fun groupNameForId(groupId: String?): CharSequence {
+ return groupNameLookup[groupId] ?: ""
+ }
+
+ fun proposeEditForChannel(channel: NotificationChannel, edit: Int) {
+ if (channel.importance == edit) {
+ edits.remove(channel)
+ } else {
+ edits[channel] = edit
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
+ return try {
+ noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
+ .list as? List<NotificationChannelGroup> ?: listOf()
+ } catch (e: Exception) {
+ Log.e(TAG, "Error fetching channel groups", e)
+ listOf()
+ }
+ }
+
+ private fun checkAreAppNotificationsOn(): Boolean {
+ return try {
+ noMan.areNotificationsEnabledForPackage(packageName!!, appUid!!)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error calling NoMan", e)
+ false
+ }
+ }
+
+ private fun applyAppNotificationsOn(b: Boolean) {
+ try {
+ noMan.setNotificationsEnabledForPackage(packageName!!, appUid!!, b)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error calling NoMan", e)
+ }
+ }
+
+ private fun setChannelImportance(channel: NotificationChannel, importance: Int) {
+ try {
+ channel.importance = importance
+ noMan.updateNotificationChannelForPackage(packageName!!, appUid!!, channel)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to update notification importance", e)
+ }
+ }
+
+ @VisibleForTesting
+ fun apply() {
+ for ((channel, importance) in edits) {
+ if (channel.importance != importance) {
+ setChannelImportance(channel, importance)
+ }
+ }
+
+ if (appNotificationsEnabled != checkAreAppNotificationsOn()) {
+ applyAppNotificationsOn(appNotificationsEnabled)
+ }
+ }
+
+ private fun initDialog() {
+ dialog = Dialog(context)
+
+ dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
+ dialog.apply {
+ setContentView(R.layout.notif_half_shelf)
+ setCanceledOnTouchOutside(true)
+ findViewById<ChannelEditorListView>(R.id.half_shelf_container).apply {
+ controller = this@ChannelEditorDialogController
+ appIcon = this@ChannelEditorDialogController.appIcon
+ appName = this@ChannelEditorDialogController.appName
+ channels = providedChannels
+ }
+
+ findViewById<TextView>(R.id.done_button)?.setOnClickListener {
+ apply()
+ done()
+ }
+
+ findViewById<TextView>(R.id.see_more_button)?.setOnClickListener {
+ onSettingsClickListener?.onClick(it, null, appUid!!)
+ dismiss()
+ }
+
+ window?.apply {
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ addFlags(wmFlags)
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL)
+ setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)
+
+ attributes = attributes.apply {
+ format = PixelFormat.TRANSLUCENT
+ title = ChannelEditorDialogController::class.java.simpleName
+ gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ width = MATCH_PARENT
+ height = WRAP_CONTENT
+ }
+ }
+ }
+ }
+
+ private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+ or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
new file mode 100644
index 0000000..7fea30c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 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.statusbar.notification.row
+
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_NONE
+import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.transition.AutoTransition
+import android.transition.TransitionManager
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.Switch
+import android.widget.TextView
+
+import com.android.systemui.R
+
+/**
+ * Half-shelf for notification channel controls
+ */
+class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
+ lateinit var controller: ChannelEditorDialogController
+ var appIcon: Drawable? = null
+ var appName: String? = null
+ var channels = mutableListOf<NotificationChannel>()
+ set(newValue) {
+ field = newValue
+ updateRows()
+ }
+
+ // The first row is for the entire app
+ private lateinit var appControlRow: AppControlView
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ appControlRow = findViewById(R.id.app_control)
+ }
+
+ private fun updateRows() {
+ val enabled = controller.appNotificationsEnabled
+
+ val transition = AutoTransition()
+ transition.duration = 200
+ TransitionManager.beginDelayedTransition(this, transition)
+
+ // Remove any rows
+ val n = childCount
+ for (i in n.downTo(0)) {
+ val child = getChildAt(i)
+ if (child is ChannelRow) {
+ removeView(child)
+ }
+ }
+
+ updateAppControlRow(enabled)
+
+ if (enabled) {
+ val inflater = LayoutInflater.from(context)
+ for (channel in channels) {
+ addChannelRow(channel, inflater)
+ }
+ }
+ }
+
+ private fun addChannelRow(channel: NotificationChannel, inflater: LayoutInflater) {
+ val row = inflater.inflate(R.layout.notif_half_shelf_row, null) as ChannelRow
+ row.controller = controller
+ row.channel = channel
+ addView(row)
+ }
+
+ private fun updateAppControlRow(enabled: Boolean) {
+ appControlRow.iconView.setImageDrawable(appIcon)
+ appControlRow.channelName.text = context.resources
+ .getString(R.string.notification_channel_dialog_title, appName)
+ appControlRow.switch.isChecked = enabled
+ appControlRow.switch.setOnCheckedChangeListener { _, b ->
+ controller.appNotificationsEnabled = b
+ updateRows()
+ }
+ }
+}
+
+class AppControlView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
+ lateinit var iconView: ImageView
+ lateinit var channelName: TextView
+ lateinit var switch: Switch
+
+ override fun onFinishInflate() {
+ iconView = findViewById(R.id.icon)
+ channelName = findViewById(R.id.app_name)
+ switch = findViewById(R.id.toggle)
+ }
+}
+
+class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
+
+ lateinit var controller: ChannelEditorDialogController
+ private lateinit var iconView: ImageView
+ private lateinit var channelName: TextView
+ private lateinit var channelDescription: TextView
+ private lateinit var switch: Switch
+ var gentle = false
+
+ var channel: NotificationChannel? = null
+ set(newValue) {
+ field = newValue
+ updateImportance()
+ updateViews()
+ }
+
+ override fun onFinishInflate() {
+ iconView = findViewById(R.id.icon)
+ channelName = findViewById(R.id.channel_name)
+ channelDescription = findViewById(R.id.channel_description)
+ switch = findViewById(R.id.toggle)
+ switch.setOnCheckedChangeListener { _, b ->
+ channel?.let {
+ controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
+ }
+ }
+ }
+
+ private fun updateViews() {
+ val nc = channel ?: return
+
+ iconView.setImageDrawable(
+ if (gentle)
+ context.getDrawable(R.drawable.ic_notification_gentle)
+ else context.getDrawable(R.drawable.ic_notification_interruptive))
+
+ channelName.text = nc.name ?: "(missing)"
+
+ nc.group?.let { groupId ->
+ channelDescription.text = controller.groupNameForId(groupId)
+ }
+
+ if (nc.group == null || TextUtils.isEmpty(channelDescription.text)) {
+ channelDescription.visibility = View.GONE
+ } else {
+ channelDescription.visibility = View.VISIBLE
+ }
+
+ switch.isChecked = nc.importance != IMPORTANCE_NONE
+ }
+
+ private fun updateImportance() {
+ val importance = channel?.importance ?: 0
+ gentle = importance != IMPORTANCE_UNSPECIFIED && importance < IMPORTANCE_DEFAULT
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 24c7b291..b3ca88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2398,6 +2398,14 @@
* it's a summary notification).
*/
public int getNumUniqueChannels() {
+ return getUniqueChannels().size();
+ }
+
+ /**
+ * Returns the channels covered by the notification row (including its children if
+ * it's a summary notification).
+ */
+ public ArraySet<NotificationChannel> getUniqueChannels() {
ArraySet<NotificationChannel> channels = new ArraySet<>();
channels.add(mEntry.channel);
@@ -2417,7 +2425,8 @@
}
}
}
- return channels.size();
+
+ return channels;
}
public void updateChildrenHeaderAppearance() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index faa7898..f15d6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -18,7 +18,6 @@
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.app.INotificationManager;
import android.app.NotificationChannel;
@@ -26,6 +25,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -125,13 +125,18 @@
* Sends an intent to open the notification settings for a particular package and optional
* channel.
*/
+ public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
private void startAppNotificationSettingsActivity(String packageName, final int appUid,
final NotificationChannel channel, ExpandableNotificationRow row) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+
if (channel != null) {
+ final Bundle args = new Bundle();
intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+ args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
}
mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
}
@@ -301,7 +306,7 @@
iNotificationManager,
packageName,
row.getEntry().channel,
- row.getNumUniqueChannels(),
+ row.getUniqueChannels(),
sbn,
mCheckSaveListener,
onSettingsClick,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index d49f168..942f566 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.util.List;
+import java.util.Set;
/**
* The guts of a notification revealed when performing a long press. This also houses the blocking
@@ -96,12 +97,14 @@
private INotificationManager mINotificationManager;
private PackageManager mPm;
private MetricsLogger mMetricsLogger;
+ private ChannelEditorDialogController mChannelEditorDialogController;
private String mPackageName;
private String mAppName;
private int mAppUid;
private String mDelegatePkg;
private int mNumUniqueChannelsInRow;
+ private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
private boolean mWasShownHighPriority;
@@ -126,6 +129,7 @@
private NotificationGuts mGutsContainer;
private Drawable mSelectedBackground;
private Drawable mUnselectedBackground;
+ private Drawable mPkgIcon;
/** Whether this view is being shown as part of the blocking helper. */
private boolean mIsForBlockingHelper;
@@ -233,7 +237,7 @@
final INotificationManager iNotificationManager,
final String pkg,
final NotificationChannel notificationChannel,
- final int numUniqueChannelsInRow,
+ final Set<NotificationChannel> uniqueChannelsInRow,
final StatusBarNotification sbn,
final CheckSaveListener checkSaveListener,
final OnSettingsClickListener onSettingsClick,
@@ -244,7 +248,7 @@
boolean wasShownHighPriority)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
- numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
+ uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */,
importance, wasShownHighPriority);
@@ -255,7 +259,7 @@
INotificationManager iNotificationManager,
String pkg,
NotificationChannel notificationChannel,
- int numUniqueChannelsInRow,
+ Set<NotificationChannel> uniqueChannelsInRow,
StatusBarNotification sbn,
CheckSaveListener checkSaveListener,
OnSettingsClickListener onSettingsClick,
@@ -268,8 +272,10 @@
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
+ mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
mPackageName = pkg;
- mNumUniqueChannelsInRow = numUniqueChannelsInRow;
+ mUniqueChannelsInRow = uniqueChannelsInRow;
+ mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
mSbn = sbn;
mPm = pm;
mAppSettingsClickListener = onAppSettingsClick;
@@ -355,7 +361,7 @@
}
View turnOffButton = findViewById(R.id.turn_off_notifications);
- turnOffButton.setOnClickListener(getSettingsOnClickListener());
+ turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
? VISIBLE : GONE);
@@ -379,7 +385,7 @@
private void bindHeader() {
// Package name
- Drawable pkgicon = null;
+ mPkgIcon = null;
ApplicationInfo info;
try {
info = mPm.getApplicationInfo(
@@ -390,13 +396,13 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
mAppName = String.valueOf(mPm.getApplicationLabel(info));
- pkgicon = mPm.getApplicationIcon(info);
+ mPkgIcon = mPm.getApplicationIcon(info);
}
} catch (PackageManager.NameNotFoundException e) {
// app is gone, just show package name and generic icon
- pkgicon = mPm.getDefaultActivityIcon();
+ mPkgIcon = mPm.getDefaultActivityIcon();
}
- ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
+ ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(mPkgIcon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
// Delegate
@@ -437,6 +443,16 @@
return null;
}
+ private OnClickListener getTurnOffNotificationsClickListener() {
+ return ((View view) -> {
+ if (mChannelEditorDialogController != null) {
+ mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
+ mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mChannelEditorDialogController.show();
+ }
+ });
+ }
+
private void bindChannelDetails() throws RemoteException {
bindName();
bindGroup();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
new file mode 100644
index 0000000..7632630
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019 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.statusbar.notification.row
+
+import android.app.INotificationManager
+import android.app.NotificationChannel
+import android.app.NotificationChannelGroup
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_NONE
+import android.content.pm.ParceledListSlice
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+
+import com.android.systemui.SysuiTestCase
+
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ChannelEditorDialogControllerTest : SysuiTestCase() {
+
+ private lateinit var controller: ChannelEditorDialogController
+ private lateinit var channel1: NotificationChannel
+ private lateinit var channel2: NotificationChannel
+ private lateinit var channelDefault: NotificationChannel
+ private lateinit var group: NotificationChannelGroup
+
+ private val appIcon = ColorDrawable(Color.MAGENTA)
+
+ @Mock
+ private lateinit var mockNoMan: INotificationManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ controller = ChannelEditorDialogController(mContext, mockNoMan)
+
+ channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
+ channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT)
+ channelDefault = NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
+
+ group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME)
+
+ `when`(mockNoMan.getNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean()))
+ .thenReturn(ParceledListSlice(listOf(group)))
+
+ `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(true)
+ }
+
+ @Test
+ fun testPrepareDialogForApp_noExtraChannels() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channel1, channel2), appIcon, clickListener)
+
+ assertEquals(2, controller.providedChannels.size)
+ }
+
+ @Test
+ fun testPrepareDialogForApp_onlyDefaultChannel() {
+ group.addChannel(channelDefault)
+
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channelDefault), appIcon, clickListener)
+
+ assertEquals("No channels should be shown when there is only the miscellaneous channel",
+ 0, controller.providedChannels.size)
+ }
+
+ @Test
+ fun testPrepareDialogForApp_noProvidedChannels_noException() {
+ group.channels = listOf()
+
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(), appIcon, clickListener)
+ }
+
+ @Test
+ fun testPrepareDialogForApp_retrievesUpto4Channels() {
+ val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT)
+ val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT)
+
+ group.channels = listOf(channel1, channel2, channel3, channel4)
+
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channel1), appIcon, clickListener)
+
+ assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
+ 4, controller.providedChannels.size)
+ }
+
+ @Test
+ fun testApply_demoteChannel() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channel1, channel2), appIcon, clickListener)
+
+ // propose an adjustment of channel1
+ controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
+
+ controller.apply()
+
+ assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_NONE, channel1.importance)
+
+ // Channel 2 shouldn't have changed
+ assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_DEFAULT, channel2.importance)
+ }
+
+ @Test
+ fun testApply_demoteApp() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channel1, channel2), appIcon, clickListener)
+
+ controller.appNotificationsEnabled = false
+ controller.apply()
+
+ verify(mockNoMan, times(1)).setNotificationsEnabledForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(false))
+ }
+
+ @Test
+ fun testApply_promoteApp() {
+ // App starts out disabled
+ `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(false)
+
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ setOf(channel1, channel2), appIcon, clickListener)
+ controller.appNotificationsEnabled = true
+ controller.apply()
+
+ verify(mockNoMan, times(1)).setNotificationsEnabledForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(true))
+ }
+
+ private val clickListener = object : NotificationInfo.OnSettingsClickListener {
+ override fun onClick(v: View, c: NotificationChannel, appUid: Int) {
+ }
+ }
+
+ companion object {
+ const val TEST_APP_NAME = "Test App Name"
+ const val TEST_PACKAGE_NAME = "test_package"
+ const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME
+ const val TEST_UID = 1
+ const val MULTIPLE_CHANNEL_COUNT = 2
+ const val TEST_CHANNEL = "test_channel"
+ const val TEST_CHANNEL_NAME = "Test Channel Name"
+ const val TEST_CHANNEL2 = "test_channel2"
+ const val TEST_CHANNEL_NAME2 = "Test Channel Name2"
+ const val TEST_GROUP_ID = "test_group_id"
+ const val TEST_GROUP_NAME = "Test Group Name"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 5f0839d..6376bd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -31,6 +31,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -317,7 +318,7 @@
any(INotificationManager.class),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anyInt(),
+ anySet(),
eq(statusBarNotification),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
@@ -345,7 +346,7 @@
any(INotificationManager.class),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anyInt(),
+ anySet(),
eq(statusBarNotification),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
@@ -375,7 +376,7 @@
any(INotificationManager.class),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anyInt(),
+ anySet(),
eq(statusBarNotification),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
@@ -404,7 +405,7 @@
any(INotificationManager.class),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anyInt(),
+ anySet(),
eq(statusBarNotification),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
@@ -432,7 +433,7 @@
any(INotificationManager.class),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anyInt(),
+ anySet(),
eq(statusBarNotification),
any(NotificationInfo.CheckSaveListener.class),
any(NotificationInfo.OnSettingsClickListener.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 7bd2580..06acc73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -20,7 +20,6 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
@@ -54,7 +53,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.metrics.LogMaker;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -70,7 +68,6 @@
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -86,6 +83,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
@SmallTest
@@ -103,6 +102,8 @@
private NotificationInfo mNotificationInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
+ private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
+ private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
@Rule
@@ -153,9 +154,11 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
+ mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
+ mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
@@ -191,7 +194,7 @@
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
null, null, null,
true, false,
IMPORTANCE_DEFAULT, true);
@@ -206,7 +209,8 @@
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ null, null, null, true, false,
IMPORTANCE_DEFAULT, true);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
@@ -215,7 +219,8 @@
@Test
public void testBindNotification_noDelegate() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ null, null, null, true, false,
IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
@@ -234,7 +239,8 @@
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
@@ -246,7 +252,8 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
@@ -261,7 +268,8 @@
eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
@@ -271,7 +279,8 @@
@Test
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
@@ -280,8 +289,8 @@
@Test
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT, true);
+ TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
+ mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -293,7 +302,8 @@
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
+ TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
+ mSbn, null, null, null, true,
false, IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
@@ -302,7 +312,8 @@
@Test
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, true,
IMPORTANCE_DEFAULT, true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
@@ -311,7 +322,7 @@
@Test
public void testBindNotification_BlockLink_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, mock(
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, mock(
NotificationInfo.OnSettingsClickListener.class), null, true, false,
true /* isBlockingHelper */, IMPORTANCE_DEFAULT, true);
final View block =
@@ -326,7 +337,7 @@
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
@@ -341,7 +352,8 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -351,7 +363,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
}, null, false, false, IMPORTANCE_DEFAULT, true);
@@ -362,10 +374,11 @@
@Test
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
}, null, true, false, IMPORTANCE_DEFAULT, true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
@@ -375,7 +388,7 @@
@Test
public void testBindNotificationLogging_notBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
null, null, null,
true, false,
IMPORTANCE_DEFAULT, true);
@@ -389,7 +402,7 @@
@Test
public void testBindNotificationLogging_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
null, null, null,
false, true,
true,
@@ -404,7 +417,7 @@
@Test
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
null, null, null,
false, true,
true,
@@ -417,7 +430,8 @@
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null,
+ TEST_PACKAGE_NAME, mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
@@ -433,7 +447,8 @@
public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
null, true, false, IMPORTANCE_DEFAULT, true);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
@@ -444,7 +459,8 @@
@UiThreadTest
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
null, true, false, IMPORTANCE_DEFAULT, true);
assertEquals(GONE, mNotificationInfo.findViewById(
R.id.interruptiveness_settings).getVisibility());
@@ -455,7 +471,8 @@
@Test
public void testBindNotification_whenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, true,
IMPORTANCE_DEFAULT, true);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
@@ -468,7 +485,8 @@
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -479,7 +497,8 @@
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -493,7 +512,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.silence).performClick();
@@ -507,7 +527,8 @@
throws Exception {
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
@@ -522,7 +543,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_UNSPECIFIED, true);
mNotificationInfo.handleCloseControls(true, false);
@@ -541,7 +563,8 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
IMPORTANCE_DEFAULT, true);
@@ -570,7 +593,8 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
false /* isNonblockable */, true /* isForBlockingHelper */,
true, IMPORTANCE_DEFAULT, true);
@@ -589,7 +613,8 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
@@ -608,7 +633,7 @@
mMockINotificationManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
- 1 /* numChannels */,
+ mNotificationChannelSet /* numChannels */,
mSbn,
null /* checkSaveListener */,
null /* onSettingsClick */,
@@ -635,7 +660,7 @@
mMockINotificationManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
- 1 /* numChannels */,
+ mNotificationChannelSet /* numChannels */,
mSbn,
null /* checkSaveListener */,
null /* onSettingsClick */,
@@ -664,7 +689,8 @@
public void testKeepUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, true,
IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.keep_showing).performClick();
@@ -683,7 +709,8 @@
public void testNoActionsUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, true,
IMPORTANCE_DEFAULT, true);
mNotificationInfo.handleCloseControls(true, false);
@@ -701,7 +728,8 @@
public void testSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_DEFAULT, true);
mNotificationInfo.findViewById(R.id.silence).performClick();
@@ -722,7 +750,8 @@
public void testUnSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -744,7 +773,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_UNSPECIFIED, true);
mNotificationInfo.findViewById(R.id.silence).performClick();
@@ -766,7 +796,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_MIN, false);
assertEquals(mContext.getString(R.string.inline_done_button),
@@ -782,7 +813,7 @@
ArgumentCaptor.forClass(NotificationChannel.class);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()& USER_LOCKED_IMPORTANCE) != 0);
+ assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_MIN, updated.getValue().getImportance());
}
@@ -791,7 +822,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_MIN, false);
assertEquals(mContext.getString(R.string.inline_done_button),
@@ -807,7 +839,7 @@
ArgumentCaptor.forClass(NotificationChannel.class);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()& USER_LOCKED_IMPORTANCE) != 0);
+ assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
}
@@ -816,7 +848,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_LOW, false);
assertEquals(mContext.getString(R.string.inline_done_button),
@@ -834,7 +867,8 @@
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -855,7 +889,8 @@
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
+ null, true, false,
IMPORTANCE_LOW, false);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -871,7 +906,7 @@
public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
}, null, null, true, false, IMPORTANCE_LOW, false
@@ -894,7 +929,7 @@
public void testCloseControls_withoutHittingApply() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
}, null, null, true, false, IMPORTANCE_LOW, false
@@ -910,7 +945,7 @@
assertFalse(mNotificationInfo.willBeRemoved());
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
}, null, null, true, false, IMPORTANCE_LOW, false
@@ -918,4 +953,21 @@
assertFalse(mNotificationInfo.willBeRemoved());
}
+
+ private Set<NotificationChannel> createMultipleChannelSet(int howMany) {
+ Set<NotificationChannel> multiChannelSet = new HashSet<>();
+ for (int i = 0; i < howMany; i++) {
+ if (i == 0) {
+ multiChannelSet.add(mNotificationChannel);
+ continue;
+ }
+
+ NotificationChannel channel = new NotificationChannel(
+ TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW);
+
+ multiChannelSet.add(channel);
+ }
+
+ return multiChannelSet;
+ }
}