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;
+    }
 }