Allow users to set app-wide Importance default

And the associated ui changes to the notification guts.

Bug: 22451710
Change-Id: I4e0b11e7b24d70b039a432c5e6cd76c9c7cc547c
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 136b810..bce5bf3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -54,6 +54,7 @@
     int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
     void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
     int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
+    void setAppImportance(String pkg, int uid, int importance);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index c9dbc79..f15c97e 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -24,35 +24,23 @@
         android:clickable="true"
         android:gravity="top|start"
         android:orientation="vertical"
-        android:paddingEnd="8dp"
+        android:paddingStart="@*android:dimen/notification_content_margin_start"
+        android:paddingEnd="@*android:dimen/notification_content_margin_end"
         android:background="@color/notification_guts_text_color" >
 
     <!-- header -->
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="@*android:dimen/notification_content_margin_start"
-            android:paddingTop="8dp"
-            android:paddingBottom="16dp" >
+            android:paddingBottom="8dp" >
 
         <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:id="@+id/notification_guts_header"
-                android:orientation="vertical"
+                android:orientation="horizontal"
                 android:layout_gravity="center_vertical|start"
                 android:layout_marginEnd="52dp">
-
-                <LinearLayout
-                        android:id="@+id/notification_guts_app_details"
-                        android:orientation="horizontal"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:clipChildren="false"
-                        android:layout_gravity="start|top"
-                        android:gravity="center_vertical"
-                        >
-
                     <ImageView
                             android:id="@android:id/icon"
                             android:layout_width="18dp"
@@ -76,22 +64,12 @@
                             android:layout_gravity="bottom|start"
                             android:visibility="gone"
                             android:textColor="#ffffff" />
-                </LinearLayout>
-
-                <TextView
-                        android:id="@+id/topic_details"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
-                        android:textColor="@color/notification_guts_text_color"
-                        android:layout_alignParentBottom="true"
-                        android:layout_alignParentStart="true" />
-            </LinearLayout>
+        </LinearLayout>
 
         <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
                 android:id="@+id/notification_inspect_item"
                 android:layout_width="52dp"
-                android:layout_height="match_parent"
+                android:layout_height="wrap_content"
                 android:layout_weight="0"
                 android:gravity="center"
                 android:layout_gravity="center_vertical|end"
@@ -103,7 +81,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center_vertical"
-            android:paddingStart="@*android:dimen/notification_content_margin_start"
             android:orientation="vertical"
             android:clickable="false"
             android:focusable="false"
@@ -116,8 +93,7 @@
                 android:textAppearance="@android:style/TextAppearance.Material.Subhead"
                 android:textColor="@color/notification_guts_text_color"
                 android:ellipsize="marquee"
-                android:fadingEdge="horizontal"
-                android:text="@*android:string/notification_importance_title"/>
+                android:fadingEdge="horizontal"/>
 
         <TextView
                 android:id="@+id/summary"
@@ -133,7 +109,7 @@
         <FrameLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:paddingTop="6dp" >
+                android:paddingTop="8dp" >
 
             <ImageView
                     android:id="@+id/low_importance"
@@ -162,5 +138,21 @@
                     android:layout_height="24dp"/>
 
         </FrameLayout>
+
+        <RadioGroup android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="8dp">
+            <RadioButton android:id="@+id/apply_to_topic"
+                         android:layout_width="wrap_content"
+                         android:layout_height="wrap_content"
+                         android:textColor="@color/notification_guts_text_color"
+                         android:visibility="gone"/>
+            <RadioButton android:id="@+id/apply_to_app"
+                         android:layout_width="wrap_content"
+                         android:layout_height="wrap_content"
+                         android:text="@string/apply_to_app"
+                         android:textColor="@color/notification_guts_text_color"
+                         android:visibility="gone"/>
+        </RadioGroup>
     </LinearLayout>
 </com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f71a71a..50e0661 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1201,4 +1201,34 @@
     <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
     <string name="enable_bluetooth_confirmation_ok">Turn on</string>
 
+    <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] -->
+    <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string>
+    <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] -->
+    <string name="apply_to_app">Apply to all notifications from this app</string>
+    <!-- Notification importance title, blocked status-->
+    <string name="blocked_importance">Blocked</string>
+    <!-- Notification importance title, low status-->
+    <string name="low_importance">Low importance</string>
+    <!-- Notification importance title, normal status-->
+    <string name="default_importance">Normal importance</string>
+    <!-- Notification importance title, high status-->
+    <string name="high_importance">High importance</string>
+    <!-- Notification importance title, max status-->
+    <string name="max_importance">Urgent importance</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
+    <string name="notification_importance_blocked">Never show these notifications</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
+    <string name="notification_importance_low">Silently show at the bottom of the notification list</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
+    <string name="notification_importance_default">Silently show these notifications</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
+    <string name="notification_importance_high">Show at the top of the notifications list and make sound</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
+    <string name="notification_importance_max">Peek onto the screen and make sound</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6d4dc872..b891c21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -947,7 +947,7 @@
         final StatusBarNotification sbn = row.getStatusBarNotification();
         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
         row.setTag(sbn.getPackageName());
-        final View guts = row.getGuts();
+        final NotificationGuts guts = row.getGuts();
         final String pkg = sbn.getPackageName();
         String appname = pkg;
         Drawable pkgicon = null;
@@ -969,8 +969,6 @@
         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
 
-        bindTopicImportance(sbn, row);
-
         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
         if (appUid >= 0) {
             final int appUidF = appUid;
@@ -983,69 +981,8 @@
         } else {
             settingsButton.setVisibility(View.GONE);
         }
-    }
 
-    private void bindTopicImportance(final StatusBarNotification sbn,
-            ExpandableNotificationRow row) {
-        final INotificationManager sINM = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        final Notification.Topic topic = sbn.getNotification().getTopic() == null
-                ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
-                        com.android.internal.R.string.default_notification_topic_label))
-                : sbn.getNotification().getTopic();
-
-        ((TextView) row.findViewById(R.id.topic_details)).setText(topic.getLabel());
-        final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
-        int importance = mNotificationData.getImportance(sbn.getKey());
-        SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
-        seekBar.setMax(4);
-        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
-            @Override
-            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                topicSummary.setText(getProgressSummary(progress));
-                if (fromUser) {
-                    try {
-                        sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
-                                progress);
-                    } catch (RemoteException e) {
-                        // :(
-                    }
-                }
-            }
-
-            @Override
-            public void onStartTrackingTouch(SeekBar seekBar) {
-                // no-op
-            }
-
-            @Override
-            public void onStopTrackingTouch(SeekBar seekBar) {
-                // no-op
-            }
-
-            private String getProgressSummary(int progress) {
-                switch (progress) {
-                    case NotificationListenerService.Ranking.IMPORTANCE_NONE:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_blocked);
-                    case NotificationListenerService.Ranking.IMPORTANCE_LOW:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_low);
-                    case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_default);
-                    case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_high);
-                    case NotificationListenerService.Ranking.IMPORTANCE_MAX:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_max);
-                    default:
-                        return "";
-                }
-            }
-        });
-        seekBar.setProgress(importance);
+        guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
     }
 
     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 0081496..57db80a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,12 +16,22 @@
 
 package com.android.systemui.statusbar;
 
+import android.app.INotificationManager;
+import android.app.Notification;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 
@@ -83,6 +93,88 @@
         }
     }
 
+    void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
+            final int importance) {
+        final INotificationManager sINM = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final Notification.Topic topic = sbn.getNotification().getTopic() == null
+                ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
+                com.android.internal.R.string.default_notification_topic_label))
+                : sbn.getNotification().getTopic();
+
+        final RadioButton applyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
+        if (sbn.getNotification().getTopic() != null) {
+            applyToTopic.setVisibility(View.VISIBLE);
+            applyToTopic.setChecked(true);
+            applyToTopic.setText(mContext.getString(R.string.apply_to_topic, topic.getLabel()));
+            row.findViewById(R.id.apply_to_app).setVisibility(View.VISIBLE);
+        }
+
+        final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
+        final TextView topicTitle = ((TextView) row.findViewById(R.id.title));
+        SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
+        seekBar.setMax(4);
+        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                updateTitleAndSummary(progress);
+                if (fromUser) {
+                    try {
+                        if (applyToTopic.isChecked()) {
+                            sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
+                                    progress);
+                        } else {
+                            sINM.setAppImportance(sbn.getPackageName(), sbn.getUid(), progress);
+                        }
+                    } catch (RemoteException e) {
+                        // :(
+                    }
+                }
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                // no-op
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                // no-op
+            }
+
+            private void updateTitleAndSummary(int progress) {
+                switch (progress) {
+                    case NotificationListenerService.Ranking.IMPORTANCE_NONE:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_blocked));
+                        topicTitle.setText(mContext.getString(R.string.blocked_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_LOW:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_low));
+                        topicTitle.setText(mContext.getString(R.string.low_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_default));
+                        topicTitle.setText(mContext.getString(R.string.default_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_high));
+                        topicTitle.setText(mContext.getString(R.string.high_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_MAX:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_max));
+                        topicTitle.setText(mContext.getString(R.string.max_importance));
+                        break;
+                }
+            }
+        });
+        seekBar.setProgress(importance);
+    }
+
     public void setActualHeight(int actualHeight) {
         mActualHeight = actualHeight;
         invalidate();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bb07e9d..0bfbd7f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1248,6 +1248,13 @@
             return mRankingHelper.getTopicImportance(pkg, uid, topic);
         }
 
+        @Override
+        public void setAppImportance(String pkg, int uid, int importance) {
+            enforceSystemOrSystemUI("Caller not system or systemui");
+            mRankingHelper.setAppImportance(pkg, uid, importance);
+            savePolicyFile();
+        }
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index acdd90a..a6c9b0d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -35,4 +35,6 @@
     void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
 
     int getTopicImportance(String packageName, int uid, Notification.Topic topic);
+
+    void setAppImportance(String packageName, int uid, int importance);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 5a31c6a..32c0ce2 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -251,6 +251,7 @@
             }
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATT_NAME, r.pkg);
+            out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
 
             if (!forBackup) {
                 out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -426,6 +427,20 @@
         updateConfig();
     }
 
+    /**
+     * Sets the default importance for all new topics that appear in the future, and resets
+     * the importance of all current topics.
+     */
+    @Override
+    public void setAppImportance(String pkgName, int uid, int importance) {
+        final Record r = getOrCreateRecord(pkgName, uid);
+        r.importance = importance;
+        for (Topic t :  r.topics.values()) {
+            t.importance = importance;
+        }
+        updateConfig();
+    }
+
     private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
         if (topic == null) {
             topic = createDefaultTopic();
@@ -435,6 +450,7 @@
             return t;
         } else {
             t = new Topic(topic);
+            t.importance = r.importance;
             r.topics.put(topic.getId(), t);
             return t;
         }
@@ -477,6 +493,8 @@
                 pw.print(" (");
                 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
+                pw.print(" importance=");
+                pw.print(Ranking.importanceToString(r.importance));
                 pw.println();
                 for (Topic t : r.topics.values()) {
                     pw.print(prefix);
@@ -532,6 +550,7 @@
 
         String pkg;
         int uid = UNKNOWN_UID;
+        int importance = DEFAULT_IMPORTANCE;
         Map<String, Topic> topics = new ArrayMap<>();
    }
 
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index cf1a4aa..fecfdf9 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -23,6 +23,7 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
 import android.os.Vibrator;
@@ -180,6 +181,9 @@
 
         new Test("with topic GoodBye") {
             public void run() {
+                Notification.BigPictureStyle picture = new Notification.BigPictureStyle();
+                picture.bigPicture(BitmapFactory.decodeResource(getResources(),
+                        R.id.large_icon_pineapple2));
                 Notification n = new Notification.Builder(NotificationTestList.this)
                         .setSmallIcon(R.drawable.icon1)
                         .setWhen(mActivityCreateTime)
@@ -187,11 +191,29 @@
                         .setContentText("This is a notification!!!")
                         .setContentIntent(makeIntent2())
                         .setTopic(new Notification.Topic("bye", "Goodbye"))
+                        .setStyle(picture)
                         .build();
 
                 mNM.notify(9999, n);
             }
         },
+        new Test("with topic Bananas") {
+            public void run() {
+                Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+                bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+                Notification n = new Notification.Builder(NotificationTestList.this)
+                        .setSmallIcon(R.drawable.icon1)
+                        .setStyle(bigText)
+                        .setWhen(mActivityCreateTime)
+                        .setContentTitle("bananananana")
+                        .setContentText("This is a banana!!!")
+                        .setContentIntent(makeIntent2())
+                        .setTopic(new Notification.Topic("bananas", "Bananas"))
+                        .build();
+
+                mNM.notify(999, n);
+            }
+        },
 
         new Test("Whens") {
             public void run()