Allow users to set notification topic importance.

In the absence of mocks, the silder is shown in the
'normal' position if the user hasn't yet
chosen an importance value.

Bug: 22854014
Change-Id: I51594959412775fe89b29af66ddcb13bafa67255
diff --git a/res/layout/preference_importance_slider.xml b/res/layout/preference_importance_slider.xml
new file mode 100644
index 0000000..737254b
--- /dev/null
+++ b/res/layout/preference_importance_slider.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:gravity="center_vertical"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:orientation="vertical"
+        android:clickable="false"
+        android:focusable="false"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip">
+
+        <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                android:textColor="?android:attr/textColorPrimary"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal" />
+
+        <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignStart="@android:id/title"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Body1"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="10"
+                android:minLines="2" />
+
+        <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="6dp">
+
+            <ImageView
+                    android:id="@+id/low_importance"
+                    android:src="@android:drawable/ic_menu_close_clear_cancel"
+                    android:layout_gravity="center_vertical|start"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp" />
+
+            <SeekBar
+                    android:id="@*android:id/seekbar"
+                    android:layout_marginStart="24dp"
+                    android:layout_marginEnd="24dp"
+                    android:layout_gravity="center_vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:focusable="true"/>
+
+            <ImageView
+                    android:id="@+id/max_importance"
+                    android:src="@android:drawable/ic_popup_reminder"
+                    android:layout_gravity="center_vertical|end"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp" />
+
+        </FrameLayout>
+
+</LinearLayout>
diff --git a/res/layout/preference_volume_slider.xml b/res/layout/preference_volume_slider.xml
index 878a710..d279eb6 100644
--- a/res/layout/preference_volume_slider.xml
+++ b/res/layout/preference_volume_slider.xml
@@ -20,7 +20,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:gravity="center_vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clickable="false" >
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8928307..89ee47b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6124,6 +6124,9 @@
     <!-- [CHAR LIMIT=20] Notification settings: App notifications dialog dismiss button caption -->
     <string name="app_notifications_dialog_done">Done</string>
 
+    <!-- [CHAR LIMIT=150] App notification settings: App notifications Importance title -->
+    <string name="app_notification_importance_title">Importance</string>
+
     <!-- [CHAR LIMIT=40] Zen mode settings: Rule name option and edit dialog title -->
     <string name="zen_mode_rule_name">Rule name</string>
 
diff --git a/res/xml/topic_notification_settings.xml b/res/xml/topic_notification_settings.xml
index 6826070..174c273 100644
--- a/res/xml/topic_notification_settings.xml
+++ b/res/xml/topic_notification_settings.xml
@@ -18,6 +18,13 @@
         android:title="@string/topic_notifications_title"
         android:key="topic_notification_settings">
 
+    <!-- Importance -->
+    <com.android.settings.notification.ImportanceSeekBarPreference
+            android:key="importance"
+            android:title="@string/app_notification_importance_title"
+            android:order="1"
+            android:persistent="false" />
+
     <!-- Bypass DND -->
     <SwitchPreference
             android:key="bypass_dnd"
diff --git a/src/com/android/settings/notification/ImportanceSeekBarPreference.java b/src/com/android/settings/notification/ImportanceSeekBarPreference.java
new file mode 100644
index 0000000..fdba42c
--- /dev/null
+++ b/src/com/android/settings/notification/ImportanceSeekBarPreference.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (C) 2015 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 com.android.settings.R;
+import com.android.settings.SeekBarPreference;
+
+import android.content.Context;
+import android.service.notification.NotificationListenerService;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+/**
+ * A slider preference that controls notification importance.
+ **/
+public class ImportanceSeekBarPreference extends SeekBarPreference implements
+        SeekBar.OnSeekBarChangeListener {
+    private static final String TAG = "ImportanceSeekBarPref";
+
+    public static final int IMPORTANCE_PROGRESS_OFFSET = 2;
+    private Callback mCallback;
+    private TextView mSummaryTextView;
+    private String mSummary;
+
+    public ImportanceSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setLayoutResource(R.layout.preference_importance_slider);
+    }
+
+    public ImportanceSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ImportanceSeekBarPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImportanceSeekBarPreference(Context context) {
+        this(context, null);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+        mSummaryTextView = (TextView) view.findViewById(com.android.internal.R.id.summary);
+    }
+
+    @Override
+    public void setProgress(int progress) {
+        mSummary = getProgressSummary(progress);
+        super.setProgress(progress);
+    }
+
+    @Override
+    public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromTouch) {
+        super.onProgressChanged(seekBar, progress, fromTouch);
+        if (mSummaryTextView != null) {
+            mSummaryTextView.setText(getProgressSummary(progress));
+        }
+        if (fromTouch) {
+            mCallback.onImportanceChanged(progress);
+        }
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return mSummary;
+    }
+
+    private String getProgressSummary(int progress) {
+        // Map progress 0-4 values to Importance's -2-2.
+        progress = progress - IMPORTANCE_PROGRESS_OFFSET;
+        switch (progress) {
+            case NotificationListenerService.Ranking.IMPORTANCE_NONE:
+                return getContext().getString(
+                        com.android.internal.R.string.notification_importance_blocked);
+            case NotificationListenerService.Ranking.IMPORTANCE_LOW:
+                return getContext().getString(
+                        com.android.internal.R.string.notification_importance_low);
+            case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
+                return getContext().getString(
+                        com.android.internal.R.string.notification_importance_default);
+            case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
+                return getContext().getString(
+                        com.android.internal.R.string.notification_importance_high);
+            case NotificationListenerService.Ranking.IMPORTANCE_MAX:
+                return getContext().getString(
+                        com.android.internal.R.string.notification_importance_max);
+            default:
+                return "";
+        }
+    }
+
+    public interface Callback {
+        void onImportanceChanged(int progress);
+    }
+}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index dcebf8b..4c1f6ea 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -62,6 +62,7 @@
         row.priority = getBypassZenMode(row.pkg, row.uid, row.topic);
         row.sensitive = getSensitive(row.pkg, row.uid, row.topic);
         row.banned = getNotificationsBanned(row.pkg, row.uid);
+        row.importance = getImportance(row.pkg, row.uid, row.topic);
         return row;
     }
 
@@ -128,6 +129,25 @@
         }
     }
 
+    public boolean setImportance(String pkg, int uid, Notification.Topic topic, int importance) {
+        try {
+            sINM.setTopicImportance(pkg, uid, topic, importance);
+            return true;
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+            return false;
+        }
+    }
+
+    public int getImportance(String pkg, int uid, Notification.Topic topic) {
+        try {
+            return sINM.getTopicImportance(pkg, uid, topic);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+            return NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+        }
+    }
+
     public List<Notification.Topic> getTopics(String pkg, int uid) {
         try {
             final ParceledListSlice<Notification.Topic> parceledList = sINM.getTopics(pkg, uid);
@@ -156,6 +176,7 @@
         public Notification.Topic topic;
         public boolean priority;
         public boolean sensitive;
+        public int importance;
     }
 
 }
diff --git a/src/com/android/settings/notification/TopicNotificationSettings.java b/src/com/android/settings/notification/TopicNotificationSettings.java
index 48c611e..71196b7 100644
--- a/src/com/android/settings/notification/TopicNotificationSettings.java
+++ b/src/com/android/settings/notification/TopicNotificationSettings.java
@@ -30,14 +30,13 @@
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.text.TextUtils;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -50,10 +49,12 @@
     protected static final String ARG_PACKAGE_INFO = "arg_info";
     private static final String KEY_BYPASS_DND = "bypass_dnd";
     private static final String KEY_SENSITIVE = "sensitive";
+    private static final String KEY_IMPORTANCE = "importance";
 
     private final NotificationBackend mBackend = new NotificationBackend();
 
     private Context mContext;
+    private ImportanceSeekBarPreference mImportance;
     private SwitchPreference mPriority;
     private SwitchPreference mSensitive;
     private TopicRow mTopicRow;
@@ -122,11 +123,27 @@
         mIsSystemPackage = Utils.isSystemPackage(pm, info);
 
         addPreferencesFromResource(R.xml.topic_notification_settings);
+        mImportance = (ImportanceSeekBarPreference) findPreference(KEY_IMPORTANCE);
         mPriority = (SwitchPreference) findPreference(KEY_BYPASS_DND);
         mSensitive = (SwitchPreference) findPreference(KEY_SENSITIVE);
 
         mTopicRow = mBackend.loadTopicRow(pm, info.applicationInfo, topic);
 
+        mImportance.setMax(4);
+        // TODO: stop defaulting to 'normal' in the UI when there are mocks for this scenario.
+        int importance =
+                mTopicRow.importance == NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED
+                ? NotificationListenerService.Ranking.IMPORTANCE_DEFAULT
+                        : mTopicRow.importance;
+        mImportance.setProgress(
+                importance + ImportanceSeekBarPreference.IMPORTANCE_PROGRESS_OFFSET);
+        mImportance.setCallback(new ImportanceSeekBarPreference.Callback() {
+            @Override
+            public void onImportanceChanged(int progress) {
+                mBackend.setImportance(mTopicRow.pkg, mTopicRow.uid, mTopicRow.topic,
+                        progress - ImportanceSeekBarPreference.IMPORTANCE_PROGRESS_OFFSET);
+            }
+        });
         mPriority.setChecked(mTopicRow.priority);
         mSensitive.setChecked(mTopicRow.sensitive);
 
@@ -167,6 +184,7 @@
         setVisible(mPriority, mIsSystemPackage || !banned);
         setVisible(mSensitive, mIsSystemPackage || !banned && lockscreenSecure
                 && lockscreenNotificationsEnabled && allowPrivate);
+        setVisible(mImportance, !banned);
     }
 
     private void setVisible(Preference p, boolean visible) {