Add settings page for notification channel groups

Bug: 63927402
Test: tests/unit/src/com/android/settings/notification/.*
Change-Id: Iebf7d8ba54f0cf5801a42f3161354d3cc5e5c848
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e3decb0..7702257 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2700,6 +2700,22 @@
                 android:value="com.android.settings.notification.AppNotificationSettings" />
         </activity>
 
+        <!-- Show channel group-level notification settings (group passed in as extras) -->
+        <activity android:name="Settings$ChannelGroupNotificationSettingsActivity"
+                  android:exported="true">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.notification.ChannelGroupNotificationSettings" />
+        </activity>
+
+
         <!-- Show channel-level notification settings (channel passed in as extras) -->
         <activity android:name="Settings$ChannelNotificationSettingsActivity"
                   android:exported="true">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ec83053..0edb017 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6835,6 +6835,9 @@
     <!-- [CHAR LIMIT=100] Notification channel title -->
     <string name="notification_channel_title">Notification category</string>
 
+    <!-- [CHAR LIMIT=200] Notification channel group title -->
+    <string name="notification_group_title">Notification category group</string>
+
     <!-- [CHAR LIMIT=100] Notification importance screen title -->
     <string name="notification_importance_title">Importance</string>
 
@@ -7002,12 +7005,21 @@
     <!-- [CHAR LIMIT=NONE] Text appearing when channel notifications are off -->
     <string name="channel_notifications_off_desc">Android is blocking this category of notifications from appearing on this device</string>
 
+    <!-- [CHAR LIMIT=NONE] Text appearing when channel group notifications are off -->
+    <string name="channel_group_notifications_off_desc">Android is blocking this group of notifications from appearing on this device</string>
+
     <!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
     <string name="notification_channels">Categories</string>
 
     <!-- [CHAR LIMIT=NONE] App notification settings: non-grouped-channels title -->
     <string name="notification_channels_other">Other</string>
 
+    <!-- [CHAR LIMIT=45] App notification settings, group summary-->
+    <plurals name="notification_group_summary">
+        <item quantity="one"><xliff:g id="count" example="1">%d</xliff:g> category</item>
+        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> categories</item>
+    </plurals>
+
     <!-- [CHAR LIMIT=NONE] App notification settings: no channels -->
     <string name="no_channels">This app has not posted any notifications</string>
 
diff --git a/res/xml/upgraded_channel_notification_settings.xml b/res/xml/upgraded_channel_notification_settings.xml
index 2cece9e..ee23435 100644
--- a/res/xml/upgraded_channel_notification_settings.xml
+++ b/res/xml/upgraded_channel_notification_settings.xml
@@ -38,6 +38,7 @@
         settings:useAdditionalSummary="true" />
 
     <PreferenceCategory
+        android:key="advanced"
         android:title="@string/advanced_apps">
 
         <!-- Visibility Override -->
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index a696172..ecdf4ba 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -124,6 +124,7 @@
     public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
     public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ChannelNotificationSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class ChannelGroupNotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ManageDomainUrlsActivity extends SettingsActivity { /* empty */ }
     public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
     public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 380c070..a03314c 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -100,6 +100,7 @@
 import com.android.settings.nfc.PaymentSettings;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.ChannelNotificationSettings;
+import com.android.settings.notification.ChannelGroupNotificationSettings;
 import com.android.settings.notification.ConfigureNotificationSettings;
 import com.android.settings.notification.NotificationAccessSettings;
 import com.android.settings.notification.NotificationStation;
@@ -209,6 +210,7 @@
             BatterySaverSettings.class.getName(),
             AppNotificationSettings.class.getName(),
             ChannelNotificationSettings.class.getName(),
+            ChannelGroupNotificationSettings.class.getName(),
             ApnSettings.class.getName(),
             ApnEditor.class.getName(),
             WifiCallingSettings.class.getName(),
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 78a0a74..29eb4a3 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroup;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -50,7 +51,6 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
@@ -105,7 +105,7 @@
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... unused) {
-                    mChannelGroupList = mBackend.getChannelGroups(mPkg, mUid).getList();
+                    mChannelGroupList = mBackend.getGroups(mPkg, mUid).getList();
                     Collections.sort(mChannelGroupList, mChannelGroupComparator);
                     return null;
                 }
@@ -115,7 +115,7 @@
                     if (getHost() == null) {
                         return;
                     }
-                    populateChannelList();
+                    populateList();
                     addAppLinkPref();
                 }
             }.execute();
@@ -144,7 +144,7 @@
         getPreferenceScreen().addPreference(pref);
     }
 
-    private void populateChannelList() {
+    private void populateList() {
         if (!mChannelGroups.isEmpty()) {
             // If there's anything in mChannelGroups, we've called populateChannelList twice.
             // Clear out existing channels and log.
@@ -166,30 +166,7 @@
             empty.setEnabled(false);
             groupCategory.addPreference(empty);
         } else {
-            for (NotificationChannelGroup group : mChannelGroupList) {
-                PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
-                if (group.getId() == null) {
-                    groupCategory.setTitle(mChannelGroupList.size() > 1
-                            ? R.string.notification_channels_other
-                            : R.string.notification_channels);
-                    groupCategory.setKey(KEY_GENERAL_CATEGORY);
-                } else {
-                    groupCategory.setTitle(group.getName());
-                    groupCategory.setKey(group.getId());
-                }
-                groupCategory.setOrderingAsAdded(true);
-                getPreferenceScreen().addPreference(groupCategory);
-                mChannelGroups.add(groupCategory);
-
-                final List<NotificationChannel> channels = group.getChannels();
-                Collections.sort(channels, mChannelComparator);
-                int N = channels.size();
-                for (int i = 0; i < N; i++) {
-                    final NotificationChannel channel = channels.get(i);
-                    populateSingleChannelPrefs(groupCategory, channel);
-                }
-            }
-
+            populateGroupList();
             int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
             if (deletedChannelCount > 0) {
                 mDeletedChannels = new FooterPreference(getPrefContext());
@@ -202,48 +179,63 @@
                 getPreferenceScreen().addPreference(mDeletedChannels);
             }
         }
-
         updateDependents(mAppRow.banned);
     }
 
-    private void populateSingleChannelPrefs(PreferenceCategory groupCategory,
-            final NotificationChannel channel) {
-        MasterSwitchPreference channelPref = new MasterSwitchPreference(
+    private void populateGroupList() {
+        PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
+        groupCategory.setTitle(R.string.notification_channels);
+        groupCategory.setKey(KEY_GENERAL_CATEGORY);
+        groupCategory.setOrderingAsAdded(true);
+        getPreferenceScreen().addPreference(groupCategory);
+        mChannelGroups.add(groupCategory);
+        for (NotificationChannelGroup group : mChannelGroupList) {
+            final List<NotificationChannel> channels = group.getChannels();
+            int N = channels.size();
+            // app defined groups with one channel and channels with no group display the channel
+            // name and no summary and link directly to the channel page unless the group is blocked
+            if ((group.getId() == null || N < 2) && !group.isBlocked()) {
+                Collections.sort(channels, mChannelComparator);
+                for (int i = 0; i < N; i++) {
+                    final NotificationChannel channel = channels.get(i);
+                    populateSingleChannelPrefs(groupCategory, channel, "");
+                }
+            } else {
+                populateGroupPreference(groupCategory, group, N);
+            }
+        }
+    }
+
+    void populateGroupPreference(PreferenceGroup parent,
+            final NotificationChannelGroup group, int channelCount) {
+        MasterSwitchPreference groupPref = new MasterSwitchPreference(
                 getPrefContext());
-        channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
-                && isChannelBlockable(mAppRow.systemApp, channel)
-                && isChannelConfigurable(channel));
-        channelPref.setKey(channel.getId());
-        channelPref.setTitle(channel.getName());
-        channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
-        channelPref.setSummary(getImportanceSummary(channel));
-        Bundle channelArgs = new Bundle();
-        channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
-        channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
-        channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
-        Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
-                ChannelNotificationSettings.class.getName(),
-                channelArgs, null, R.string.notification_channel_title, null, false,
+        groupPref.setSwitchEnabled(mSuspendedAppsAdmin == null
+                && isChannelGroupBlockable(group));
+        groupPref.setKey(group.getId());
+        groupPref.setTitle(group.getName());
+        groupPref.setChecked(!group.isBlocked());
+        groupPref.setSummary(getResources().getQuantityString(
+                R.plurals.notification_group_summary, channelCount, channelCount));
+        Bundle groupArgs = new Bundle();
+        groupArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+        groupArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+        groupArgs.putString(Settings.EXTRA_CHANNEL_GROUP_ID, group.getId());
+        Intent groupIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+                ChannelGroupNotificationSettings.class.getName(),
+                groupArgs, null, R.string.notification_group_title, null, false,
                 getMetricsCategory());
-        channelPref.setIntent(channelIntent);
+        groupPref.setIntent(groupIntent);
 
-        channelPref.setOnPreferenceChangeListener(
-                new Preference.OnPreferenceChangeListener() {
-                    @Override
-                    public boolean onPreferenceChange(Preference preference,
-                            Object o) {
-                        boolean value = (Boolean) o;
-                        int importance = value ?  IMPORTANCE_LOW : IMPORTANCE_NONE;
-                        channel.setImportance(importance);
-                        channel.lockFields(
-                                NotificationChannel.USER_LOCKED_IMPORTANCE);
-                        channelPref.setSummary(getImportanceSummary(channel));
-                        mBackend.updateChannel(mPkg, mUid, channel);
+        groupPref.setOnPreferenceChangeListener(
+                (preference, o) -> {
+                    boolean value = (Boolean) o;
+                    group.setBlocked(!value);
+                    mBackend.updateChannelGroup(mPkg, mUid, group);
 
-                        return true;
-                    }
+                    return true;
                 });
-        groupCategory.addPreference(channelPref);
+        parent.addPreference(groupPref);
     }
 
     void setupBadge() {
@@ -330,38 +322,6 @@
         }
     }
 
-    private String getImportanceSummary(NotificationChannel channel) {
-        switch (channel.getImportance()) {
-            case NotificationManager.IMPORTANCE_UNSPECIFIED:
-                return getContext().getString(R.string.notification_importance_unspecified);
-            case NotificationManager.IMPORTANCE_NONE:
-                return getContext().getString(R.string.notification_toggle_off);
-            case NotificationManager.IMPORTANCE_MIN:
-                return getContext().getString(R.string.notification_importance_min);
-            case NotificationManager.IMPORTANCE_LOW:
-                return getContext().getString(R.string.notification_importance_low);
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                return getContext().getString(R.string.notification_importance_default);
-            case NotificationManager.IMPORTANCE_HIGH:
-            case NotificationManager.IMPORTANCE_MAX:
-            default:
-                return getContext().getString(R.string.notification_importance_high);
-        }
-
-    }
-
-    private Comparator<NotificationChannel> mChannelComparator =
-            new Comparator<NotificationChannel>() {
-
-        @Override
-        public int compare(NotificationChannel left, NotificationChannel right) {
-            if (left.isDeleted() != right.isDeleted()) {
-                return Boolean.compare(left.isDeleted(), right.isDeleted());
-            }
-            return left.getId().compareTo(right.getId());
-        }
-    };
-
     private Comparator<NotificationChannelGroup> mChannelGroupComparator =
             new Comparator<NotificationChannelGroup>() {
 
diff --git a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
new file mode 100644
index 0000000..7837ec8
--- /dev/null
+++ b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.Activity;
+import android.app.NotificationChannel;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.widget.FooterPreference;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
+    private static final String TAG = "ChannelGroupSettings";
+
+    private static String KEY_DELETED = "deleted";
+
+    private EntityHeaderController mHeaderPref;
+    private List<Preference> mChannels = new ArrayList();
+    private FooterPreference mDeletedChannels;
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.NOTIFICATION_CHANNEL_GROUP;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannelGroup == null) {
+            Log.w(TAG, "Missing package or uid or packageinfo or group");
+            finish();
+            return;
+        }
+
+        if (getPreferenceScreen() != null) {
+            getPreferenceScreen().removeAll();
+        }
+        addPreferencesFromResource(R.xml.notification_settings);
+        setupBlock();
+        addHeaderPref();
+        addAppLinkPref();
+        addFooterPref();
+        populateChannelList();
+
+        updateDependents(mChannelGroup.isBlocked());
+    }
+
+    @Override
+    void setupBadge() {
+
+    }
+
+    private void populateChannelList() {
+        if (!mChannels.isEmpty()) {
+            // If there's anything in mChannels, we've called populateChannelList twice.
+            // Clear out existing channels and log.
+            Log.w(TAG, "Notification channel group posted twice to settings - old size " +
+                    mChannels.size() + ", new size " + mChannels.size());
+            for (Preference p : mChannels) {
+                getPreferenceScreen().removePreference(p);
+            }
+        }
+        if (mChannelGroup.getChannels().isEmpty()) {
+            Preference empty = new Preference(getPrefContext());
+            empty.setTitle(R.string.no_channels);
+            empty.setEnabled(false);
+            getPreferenceScreen().addPreference(empty);
+            mChannels.add(empty);
+
+        } else {
+            final List<NotificationChannel> channels = mChannelGroup.getChannels();
+            Collections.sort(channels, mChannelComparator);
+            for (NotificationChannel channel : channels) {
+                mChannels.add(populateSingleChannelPrefs(
+                        getPreferenceScreen(), channel, getImportanceSummary(channel)));
+            }
+
+            int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
+            if (deletedChannelCount > 0) {
+                mDeletedChannels = new FooterPreference(getPrefContext());
+                mDeletedChannels.setSelectable(false);
+                mDeletedChannels.setTitle(getResources().getQuantityString(
+                        R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+                mDeletedChannels.setEnabled(false);
+                mDeletedChannels.setKey(KEY_DELETED);
+                mDeletedChannels.setOrder(ORDER_LAST);
+                getPreferenceScreen().addPreference(mDeletedChannels);
+                mChannels.add(mDeletedChannels);
+            }
+        }
+
+        updateDependents(mAppRow.banned);
+    }
+
+    private void addHeaderPref() {
+        ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<>();
+        rows.put(mAppRow.pkg, mAppRow);
+        collectConfigActivities(rows);
+        final Activity activity = getActivity();
+        mHeaderPref = EntityHeaderController
+                .newInstance(activity, this /* fragment */, null /* header */)
+                .setRecyclerView(getListView(), getLifecycle());
+        final Preference pref = mHeaderPref
+                .setIcon(mAppRow.icon)
+                .setLabel(mChannelGroup.getName())
+                .setSummary(mAppRow.label)
+                .setPackageName(mAppRow.pkg)
+                .setUid(mAppRow.uid)
+                .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
+                        EntityHeaderController.ActionType.ACTION_NONE)
+                .setHasAppInfoLink(true)
+                .done(activity, getPrefContext());
+        getPreferenceScreen().addPreference(pref);
+    }
+
+    private void addFooterPref() {
+        if (!TextUtils.isEmpty(mChannelGroup.getDescription())) {
+            FooterPreference descPref = new FooterPreference(getPrefContext());
+            descPref.setOrder(ORDER_LAST);
+            descPref.setSelectable(false);
+            descPref.setTitle(mChannelGroup.getDescription());
+            getPreferenceScreen().addPreference(descPref);
+            mChannels.add(descPref);
+        }
+    }
+
+    private void setupBlock() {
+        View switchBarContainer = LayoutInflater.from(
+                getPrefContext()).inflate(R.layout.styled_switch_bar, null);
+        mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
+        mSwitchBar.show();
+        mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
+        mSwitchBar.setChecked(!mChannelGroup.isBlocked());
+        mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
+            mChannelGroup.setBlocked(!isChecked);
+            mBackend.updateChannelGroup(mPkg, mUid, mChannelGroup);
+            updateDependents(!isChecked);
+        });
+
+        mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
+        mBlockBar.setOrder(ORDER_FIRST);
+        mBlockBar.setKey(KEY_BLOCK);
+        getPreferenceScreen().addPreference(mBlockBar);
+
+        if (!isChannelGroupBlockable(mChannelGroup)) {
+            setVisible(mBlockBar, false);
+        }
+
+        setupBlockDesc(R.string.channel_group_notifications_off_desc);
+    }
+
+    protected void updateDependents(boolean banned) {
+        for (Preference channel : mChannels) {
+            setVisible(channel, !banned);
+        }
+        if (mAppLink != null) {
+            setVisible(mAppLink, !banned);
+        }
+        setVisible(mBlockBar, isChannelGroupBlockable(mChannelGroup));
+        setVisible(mBlockedDesc, mAppRow.banned || mChannelGroup.isBlocked());
+    }
+}
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 2f95dd2..41f9801 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -26,6 +26,7 @@
 import android.os.AsyncTask;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
 import android.text.TextUtils;
 import android.text.BidiFormatter;
 import android.text.SpannableStringBuilder;
@@ -57,6 +58,7 @@
     private static final String KEY_VIBRATE = "vibrate";
     private static final String KEY_RINGTONE = "ringtone";
     private static final String KEY_IMPORTANCE = "importance";
+    private static final String KEY_ADVANCED = "advanced";
 
     private Preference mImportance;
     private RestrictedSwitchPreference mLights;
@@ -65,6 +67,7 @@
     private FooterPreference mFooter;
     private NotificationChannelGroup mChannelGroup;
     private EntityHeaderController mHeaderPref;
+    private PreferenceGroup mAdvanced;
 
     @Override
     public int getMetricsCategory() {
@@ -96,24 +99,10 @@
             populateUpgradedChannelPrefs();
 
             if (mChannel.getGroup() != null) {
-                // Go look up group name
-                new AsyncTask<Void, Void, Void>() {
-                    @Override
-                    protected Void doInBackground(Void... unused) {
-                        if (mChannel.getGroup() != null) {
-                            mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    protected void onPostExecute(Void unused) {
-                        if (getHost() == null || mChannelGroup == null) {
-                            return;
-                        }
-                        setChannelGroupLabel(mChannelGroup.getName());
-                    }
-                }.execute();
+                mChannelGroup = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
+                if (mChannelGroup != null) {
+                    setChannelGroupLabel(mChannelGroup.getName());
+                }
             }
         }
 
@@ -129,6 +118,7 @@
         setupVibrate();
         setupRingtone();
         setupImportance();
+        mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
     }
 
     private void addHeaderPref() {
@@ -272,7 +262,7 @@
         mBlockBar.setKey(KEY_BLOCK);
         getPreferenceScreen().addPreference(mBlockBar);
 
-        if (!isChannelBlockable(mAppRow.systemApp, mChannel)) {
+        if (!isChannelBlockable(mChannel)) {
             setVisible(mBlockBar, false);
         }
 
@@ -373,6 +363,7 @@
         if (mShowLegacyChannelConfig) {
             setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
         } else {
+            setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
             setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
             setVisible(mLights, checkCanBeVisible(
                     NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 82e3a9e..4de528e 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -136,8 +136,7 @@
         }
     }
 
-
-    public NotificationChannelGroup getGroup(String groupId, String pkg, int uid) {
+    public NotificationChannelGroup getGroup(String pkg, int uid, String groupId) {
         if (groupId == null) {
             return null;
         }
@@ -149,7 +148,19 @@
         }
     }
 
-    public ParceledListSlice<NotificationChannelGroup> getChannelGroups(String pkg, int uid) {
+    public NotificationChannelGroup getGroupWithChannels(String pkg, int uid, String groupId) {
+        if (groupId == null) {
+            return null;
+        }
+        try {
+            return sINM.getPopulatedNotificationChannelGroupForPackage(pkg, uid, groupId, true);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+            return null;
+        }
+    }
+
+    public ParceledListSlice<NotificationChannelGroup> getGroups(String pkg, int uid) {
         try {
             return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
         } catch (Exception e) {
@@ -166,6 +177,15 @@
         }
     }
 
+    public void updateChannelGroup(String pkg, int uid, NotificationChannelGroup group) {
+        try {
+            sINM.updateNotificationChannelGroupForPackage(pkg, uid, group);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+        }
+    }
+
+
     public int getDeletedChannelCount(String pkg, int uid) {
         try {
             return sINM.getDeletedChannelCount(pkg, uid);
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 3849882..8c70a20 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -24,8 +24,10 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settings.widget.SwitchBar;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedSwitchPreference;
@@ -33,6 +35,7 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -51,8 +54,8 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
-import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -61,6 +64,7 @@
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 
 abstract public class NotificationSettingsBase extends SettingsPreferenceFragment {
@@ -106,6 +110,7 @@
     protected EnforcedAdmin mSuspendedAppsAdmin;
     protected boolean mDndVisualEffectsSuppressed;
 
+    protected NotificationChannelGroup mChannelGroup;
     protected NotificationChannel mChannel;
     protected NotificationBackend.AppRow mAppRow;
     protected boolean mShowLegacyChannelConfig = false;
@@ -185,6 +190,11 @@
         mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
                 mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
 
+        mChannelGroup = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID)) ?
+                mBackend.getGroupWithChannels(mPkg, mUid,
+                        args.getString(Settings.EXTRA_CHANNEL_GROUP_ID))
+                : null;
+
         mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
                 mContext, mPkg, mUserId);
         NotificationManager.Policy policy = mNm.getNotificationPolicy();
@@ -249,6 +259,10 @@
             if (mChannel != null) {
                 row.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
             }
+            if (mChannelGroup != null) {
+                row.settingsIntent.putExtra(
+                        Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getId());
+            }
         }
     }
 
@@ -276,7 +290,7 @@
     protected void addAppLinkPref() {
         if (mAppRow.settingsIntent != null && mAppLink == null) {
             addPreferencesFromResource(R.xml.inapp_notification_settings);
-            mAppLink = (Preference) findPreference(KEY_APP_LINK);
+            mAppLink = findPreference(KEY_APP_LINK);
             mAppLink.setIntent(mAppRow.settingsIntent);
         }
     }
@@ -392,16 +406,56 @@
     }
 
     protected void setupBlockDesc(int summaryResId) {
-        mBlockedDesc = (FooterPreference) getPreferenceScreen().findPreference(
-                KEY_BLOCKED_DESC);
         mBlockedDesc = new FooterPreference(getPrefContext());
         mBlockedDesc.setSelectable(false);
         mBlockedDesc.setTitle(summaryResId);
         mBlockedDesc.setEnabled(false);
         mBlockedDesc.setOrder(50);
+        mBlockedDesc.setKey(KEY_BLOCKED_DESC);
         getPreferenceScreen().addPreference(mBlockedDesc);
     }
 
+    protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
+            final NotificationChannel channel, String summary) {
+        MasterSwitchPreference channelPref = new MasterSwitchPreference(
+                getPrefContext());
+        channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
+                && isChannelBlockable(channel)
+                && isChannelConfigurable(channel));
+        channelPref.setKey(channel.getId());
+        channelPref.setTitle(channel.getName());
+        channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
+        channelPref.setSummary(summary);
+        Bundle channelArgs = new Bundle();
+        channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+        channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+        channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
+        Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+                ChannelNotificationSettings.class.getName(),
+                channelArgs, null, R.string.notification_channel_title, null, false,
+                getMetricsCategory());
+        channelPref.setIntent(channelIntent);
+
+        channelPref.setOnPreferenceChangeListener(
+                new Preference.OnPreferenceChangeListener() {
+                    @Override
+                    public boolean onPreferenceChange(Preference preference,
+                            Object o) {
+                        boolean value = (Boolean) o;
+                        int importance = value ?  IMPORTANCE_LOW : IMPORTANCE_NONE;
+                        channel.setImportance(importance);
+                        channel.lockFields(
+                                NotificationChannel.USER_LOCKED_IMPORTANCE);
+                        channelPref.setSummary(summary);
+                        mBackend.updateChannel(mPkg, mUid, channel);
+
+                        return true;
+                    }
+                });
+        parent.addPreference(channelPref);
+        return channelPref;
+    }
+
     protected boolean checkCanBeVisible(int minImportanceVisible) {
         int importance = mChannel.getImportance();
         if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
@@ -410,6 +464,26 @@
         return importance >= minImportanceVisible;
     }
 
+    protected String getImportanceSummary(NotificationChannel channel) {
+        switch (channel.getImportance()) {
+            case NotificationManager.IMPORTANCE_UNSPECIFIED:
+                return getContext().getString(R.string.notification_importance_unspecified);
+            case NotificationManager.IMPORTANCE_NONE:
+                return getContext().getString(R.string.notification_toggle_off);
+            case NotificationManager.IMPORTANCE_MIN:
+                return getContext().getString(R.string.notification_importance_min);
+            case NotificationManager.IMPORTANCE_LOW:
+                return getContext().getString(R.string.notification_importance_low);
+            case NotificationManager.IMPORTANCE_DEFAULT:
+                return getContext().getString(R.string.notification_importance_default);
+            case NotificationManager.IMPORTANCE_HIGH:
+            case NotificationManager.IMPORTANCE_MAX:
+            default:
+                return getContext().getString(R.string.notification_importance_high);
+        }
+
+    }
+
     private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
             CharSequence entryValue, int keyguardNotificationFeatures) {
         RestrictedLockUtils.EnforcedAdmin admin =
@@ -459,7 +533,7 @@
         return !channel.getId().equals(mAppRow.lockedChannelId);
     }
 
-    protected boolean isChannelBlockable(boolean systemApp, NotificationChannel channel) {
+    protected boolean isChannelBlockable(NotificationChannel channel) {
         if (!mAppRow.systemApp) {
             return true;
         }
@@ -468,6 +542,14 @@
                 || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
     }
 
+    protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
+        if (!mAppRow.systemApp) {
+            return true;
+        }
+
+        return group.isBlocked();
+    }
+
     protected void startListeningToPackageRemove() {
         if (mListeningToPackageRemove) {
             return;
@@ -501,4 +583,12 @@
             }
         }
     };
+
+    protected Comparator<NotificationChannel> mChannelComparator =
+            (left, right) -> {
+                if (left.isDeleted() != right.isDeleted()) {
+                    return Boolean.compare(left.isDeleted(), right.isDeleted());
+                }
+                return left.getId().compareTo(right.getId());
+            };
 }
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 695342e..a08536a 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -23,6 +23,7 @@
 com.android.settings.deviceinfo.Status
 com.android.settings.datausage.DataSaverSummary
 com.android.settings.notification.ChannelNotificationSettings
+com.android.settings.notification.ChannelGroupNotificationSettings
 com.android.settings.datausage.AppDataUsage
 com.android.settings.accessibility.FontSizePreferenceFragmentForSetupWizard
 com.android.settings.applications.ManageDomainUrls
diff --git a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
index 22e98c7..16a0b43 100644
--- a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
+++ b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
@@ -16,7 +16,29 @@
 
 package com.android.settings.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import android.support.test.espresso.intent.Intents;
+
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
 import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
@@ -29,12 +51,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.Matchers.allOf;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class AppNotificationSettingsTest {
@@ -42,10 +58,29 @@
     private Context mTargetContext;
     private Instrumentation mInstrumentation;
 
+    NotificationManager mNm;
+    private NotificationChannelGroup mGroup1;
+    private NotificationChannel mGroup1Channel1;
+    private NotificationChannel mGroup1Channel2;
+    private NotificationChannelGroup mGroup2;
+    private NotificationChannel mGroup2Channel1;
+    private NotificationChannel mUngroupedChannel;
+
     @Before
     public void setUp() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mTargetContext = mInstrumentation.getTargetContext();
+        mNm  = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        mGroup1 = new NotificationChannelGroup(this.getClass().getName() + "1", "group1");
+        mGroup2 = new NotificationChannelGroup(this.getClass().getName() + "2", "group2");
+        mNm.createNotificationChannelGroup(mGroup1);
+        mNm.createNotificationChannelGroup(mGroup2);
+
+        mGroup1Channel1 = createChannel(mGroup1, this.getClass().getName()+ "c1-1");
+        mGroup1Channel2 = createChannel(mGroup1, this.getClass().getName()+ "c1-2");
+        mGroup2Channel1 = createChannel(mGroup2, this.getClass().getName()+ "c2-1");
+        mUngroupedChannel = createChannel(null, this.getClass().getName()+ "c");
     }
 
     @Test
@@ -60,4 +95,72 @@
                 .check(doesNotExist());
     }
 
+    @Test
+    public void launchNotificationSetting_showGroupsWithMultipleChannels() {
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+        mInstrumentation.startActivitySync(intent);
+        onView(allOf(withText(mGroup1.getName().toString()))).check(
+                matches(isDisplayed()));
+        try {
+            onView(allOf(withText(mGroup1Channel1.getName().toString())))
+                    .check(matches(isDisplayed()));
+            fail("Channel erroneously appearing");
+        } catch (Exception e) {
+            // expected
+        }
+        // links to group page
+        Intents.init();
+        onView(allOf(withText(mGroup1.getName().toString()))).perform(click());
+        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT,
+                ChannelGroupNotificationSettings.class.getName())));
+        Intents.release();
+    }
+
+    @Test
+    public void launchNotificationSetting_showUngroupedChannels() {
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+        mInstrumentation.startActivitySync(intent);
+        onView(allOf(withText(mUngroupedChannel.getName().toString())))
+                .check(matches(isDisplayed()));
+        // links directly to channel page
+        Intents.init();
+        onView(allOf(withText(mUngroupedChannel.getName().toString()))).perform(click());
+        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
+        Intents.release();
+    }
+
+    @Test
+    public void launchNotificationSetting_showGroupsWithOneChannel() {
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+        mInstrumentation.startActivitySync(intent);
+
+        onView(allOf(withText(mGroup2Channel1.getName().toString())))
+                .check(matches(isDisplayed()));
+        try {
+            onView(allOf(withText(mGroup2.getName().toString()))).check(
+                    matches(isDisplayed()));
+            fail("Group erroneously appearing");
+        } catch (Exception e) {
+            // expected
+        }
+
+        // links directly to channel page
+        Intents.init();
+        onView(allOf(withText(mGroup2Channel1.getName().toString()))).perform(click());
+        intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
+        Intents.release();
+    }
+
+    private NotificationChannel createChannel(NotificationChannelGroup group,
+            String id) {
+        NotificationChannel channel = new NotificationChannel(id, id, IMPORTANCE_DEFAULT);
+        if (group != null) {
+            channel.setGroup(group.getId());
+        }
+        mNm.createNotificationChannel(channel);
+        return channel;
+    }
 }
diff --git a/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java
new file mode 100644
index 0000000..ce2c408
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
+import android.app.INotificationManager;
+import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ChannelGroupNotificationSettingsTest {
+
+    private Context mTargetContext;
+    private Instrumentation mInstrumentation;
+    private NotificationManager mNm;
+
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mTargetContext = mInstrumentation.getTargetContext();
+        mNm  = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    @Test
+    public void launchNotificationSetting_displaysChannels() {
+        NotificationChannelGroup group =
+                new NotificationChannelGroup(this.getClass().getName(), this.getClass().getName());
+        group.setDescription("description");
+        NotificationChannel channel = new NotificationChannel(this.getClass().getName(),
+                "channel" + this.getClass().getName(), IMPORTANCE_MIN);
+        channel.setGroup(this.getClass().getName());
+        NotificationChannel channel2 = new NotificationChannel("2"+this.getClass().getName(),
+                "2channel" + this.getClass().getName(), IMPORTANCE_MIN);
+        channel2.setGroup(this.getClass().getName());
+
+        mNm.createNotificationChannelGroup(group);
+        mNm.createNotificationChannel(channel);
+        mNm.createNotificationChannel(channel2);
+
+        final Intent intent = new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .putExtra(Settings.EXTRA_CHANNEL_GROUP_ID, group.getId());
+
+        mInstrumentation.startActivitySync(intent);
+
+        onView(allOf(withText(group.getName().toString()))).check(matches(isDisplayed()));
+        onView(allOf(withText(channel.getName().toString()))).check(
+                matches(isDisplayed()));
+        onView(allOf(withText(group.getDescription().toString()))).check(
+                matches(isDisplayed()));
+        onView(allOf(withText(channel2.getName().toString()))).check(
+                matches(isDisplayed()));
+        try {
+            onView(allOf(withText("Android is blocking this group of notifications from"
+                    + " appearing on this device"))).check(matches(isDisplayed()));
+            fail("Blocking footer erroneously appearing");
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void launchNotificationSettings_blockedGroup() throws Exception {
+        NotificationChannelGroup blocked =
+                new NotificationChannelGroup("blocked", "blocked");
+        NotificationChannel channel =
+                new NotificationChannel("channel", "channel", IMPORTANCE_HIGH);
+        channel.setGroup(blocked.getId());
+        mNm.createNotificationChannelGroup(blocked);
+        mNm.createNotificationChannel(channel);
+
+        INotificationManager sINM = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        blocked.setBlocked(true);
+        sINM.updateNotificationChannelGroupForPackage(
+                mTargetContext.getPackageName(), Process.myUid(), blocked);
+
+        final Intent intent = new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .putExtra(Settings.EXTRA_CHANNEL_GROUP_ID, blocked.getId());
+        mInstrumentation.startActivitySync(intent);
+
+        onView(allOf(withText("Off"), isDisplayed())).check(matches(isDisplayed()));
+        onView(allOf(withText("Android is blocking this group of notifications from"
+                + " appearing on this device"))).check(matches(isDisplayed()));
+
+        try {
+            onView(allOf(withText(channel.getName().toString()))).check(matches(isDisplayed()));
+            fail("settings appearing for blocked group");
+        } catch (Exception e) {
+            // expected
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
new file mode 100644
index 0000000..1244dcd
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
+import android.app.INotificationManager;
+import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ChannelNotificationSettingsTest {
+
+    private Context mTargetContext;
+    private Instrumentation mInstrumentation;
+    private NotificationChannel mNotificationChannel;
+    private NotificationManager mNm;
+
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mTargetContext = mInstrumentation.getTargetContext();
+
+        mNm  = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotificationChannel = new NotificationChannel(this.getClass().getName(),
+                this.getClass().getName(), IMPORTANCE_MIN);
+        mNm.createNotificationChannel(mNotificationChannel);
+    }
+
+    @Test
+    public void launchNotificationSetting_shouldNotCrash() {
+        final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .putExtra(Settings.EXTRA_CHANNEL_ID, mNotificationChannel.getId());
+        mInstrumentation.startActivitySync(intent);
+
+        onView(allOf(withText(mNotificationChannel.getName().toString()))).check(
+                matches(isDisplayed()));
+    }
+
+    @Test
+    public void launchNotificationSettings_blockedChannel() throws Exception {
+        NotificationChannel blocked =
+                new NotificationChannel("blocked", "blocked", IMPORTANCE_NONE);
+        mNm.createNotificationChannel(blocked);
+
+        INotificationManager sINM = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        blocked.setImportance(IMPORTANCE_NONE);
+        sINM.updateNotificationChannelForPackage(
+                mTargetContext.getPackageName(), Process.myUid(), blocked);
+
+        final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+                .putExtra(Settings.EXTRA_CHANNEL_ID, blocked.getId());
+        mInstrumentation.startActivitySync(intent);
+
+        onView(allOf(withText("Off"), isDisplayed())).check(matches(isDisplayed()));
+        onView(allOf(withText("Android is blocking this category of notifications from"
+                + " appearing on this device"))).check(matches(isDisplayed()));
+
+        try {
+            onView(allOf(withText("On the lock screen"))).check(matches(isDisplayed()));
+            fail("settings appearing for blocked channel");
+        } catch (Exception e) {
+            // expected
+        }
+    }
+}