Merge "Allow conversations to be demoted out of the conversation space"
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index bdc7b99..78f9cc8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -105,6 +105,7 @@
     private static final String ATT_ORIG_IMP = "orig_imp";
     private static final String ATT_PARENT_CHANNEL = "parent";
     private static final String ATT_CONVERSATION_ID = "conv_id";
+    private static final String ATT_DEMOTE = "dem";
     private static final String DELIMITER = ",";
 
     /**
@@ -194,6 +195,7 @@
     private boolean mImportanceLockedDefaultApp;
     private String mParentId = null;
     private String mConversationId = null;
+    private boolean mDemoted = false;
 
     /**
      * Creates a notification channel.
@@ -260,6 +262,7 @@
         mOriginalImportance = in.readInt();
         mParentId = in.readString();
         mConversationId = in.readString();
+        mDemoted = in.readBoolean();
     }
 
     @Override
@@ -317,6 +320,7 @@
         dest.writeInt(mOriginalImportance);
         dest.writeString(mParentId);
         dest.writeString(mConversationId);
+        dest.writeBoolean(mDemoted);
     }
 
     /**
@@ -386,8 +390,6 @@
         return input;
     }
 
-    // Modifiable by apps on channel creation.
-
     /**
      * @hide
      */
@@ -395,6 +397,8 @@
         mId = id;
     }
 
+    // Modifiable by apps on channel creation.
+
     /**
      * Sets what group this channel belongs to.
      *
@@ -767,6 +771,20 @@
     }
 
     /**
+     * @hide
+     */
+    public void setDemoted(boolean demoted) {
+        mDemoted = demoted;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isDemoted() {
+        return mDemoted;
+    }
+
+    /**
      * Returns whether the user has chosen the importance of this channel, either to affirm the
      * initial selection from the app, or changed it to be higher or lower.
      * @see #getImportance()
@@ -829,6 +847,7 @@
         setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
         setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
                 parser.getAttributeValue(null, ATT_CONVERSATION_ID));
+        setDemoted(safeBool(parser, ATT_DEMOTE, false));
     }
 
     @Nullable
@@ -959,6 +978,9 @@
         if (getConversationId() != null) {
             out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
         }
+        if (isDemoted()) {
+            out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
+        }
 
         // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
         // truth and so aren't written to this xml file
@@ -1118,7 +1140,8 @@
                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
                 && mOriginalImportance == that.mOriginalImportance
                 && Objects.equals(getParentChannelId(), that.getParentChannelId())
-                && Objects.equals(getConversationId(), that.getConversationId());
+                && Objects.equals(getConversationId(), that.getConversationId())
+                && isDemoted() == that.isDemoted();
     }
 
     @Override
@@ -1129,7 +1152,7 @@
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
                 getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
                 mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
-                mParentId, mConversationId);
+                mParentId, mConversationId, mDemoted);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1176,7 +1199,8 @@
                 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
                 + ", mOriginalImp=" + mOriginalImportance
                 + ", mParent=" + mParentId
-                + ", mConversationId=" + mConversationId;
+                + ", mConversationId=" + mConversationId
+                + ", mDemoted=" + mDemoted;
     }
 
     /** @hide */
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index c191a0d..eb4af1c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -61,7 +61,7 @@
         DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
         DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
         DEFAULT_FLAGS.put("settings_conditionals", "false");
-        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 820c042..f7fe064 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection
 
+import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_MIN
 import android.service.notification.NotificationListenerService.Ranking
@@ -191,9 +192,9 @@
     }
 
     private fun NotificationEntry.isPeopleNotification() =
-            sbn.isPeopleNotification()
-    private fun StatusBarNotification.isPeopleNotification() =
-            peopleNotificationIdentifier.isPeopleNotification(this)
+            sbn.isPeopleNotification(channel)
+    private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
+            peopleNotificationIdentifier.isPeopleNotification(this, channel)
 
     private fun NotificationEntry.isHighPriority() =
             highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 3cc5e62..ccd7fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -99,7 +99,8 @@
     }
 
     private boolean isPeopleNotification(NotificationEntry entry) {
-        return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
+        return mPeopleNotificationIdentifier.isPeopleNotification(
+                entry.getSbn(), entry.getChannel());
     }
 
     private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 452d1eb..5c90211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -18,13 +18,14 @@
 
 import android.app.Notification
 import android.content.Context
+import android.app.NotificationChannel
 import android.service.notification.StatusBarNotification
 import android.util.FeatureFlagUtils
 import javax.inject.Inject
 import javax.inject.Singleton
 
 interface PeopleNotificationIdentifier {
-    fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+    fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
 }
 
 @Singleton
@@ -33,12 +34,13 @@
     private val context: Context
 ) : PeopleNotificationIdentifier {
 
-    override fun isPeopleNotification(sbn: StatusBarNotification) =
-            (sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
+    override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) =
+            ((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
                     (sbn.notification.shortcutId != null ||
                             FeatureFlagUtils.isEnabled(
                                     context,
                                     FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
                             ))) ||
-                    personExtractor.isPersonNotification(sbn)
+                    personExtractor.isPersonNotification(sbn)) &&
+                    !channel.isDemoted
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index ec420f3..3b106cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -25,6 +25,7 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
+import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
 import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
@@ -61,6 +62,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -151,6 +153,11 @@
       closeControls(v, true);
     };
 
+    private OnClickListener mOnDemoteClick = v -> {
+        mSelectedAction = ACTION_DEMOTE;
+        closeControls(v, true);
+    };
+
     public NotificationConversationInfo(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -295,6 +302,8 @@
                     mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
         }
 
+        ImageButton demote = findViewById(R.id.demote);
+        demote.setOnClickListener(mOnDemoteClick);
     }
 
     private void bindHeader() {
@@ -609,7 +618,7 @@
                         }
                         break;
                     case ACTION_DEMOTE:
-                        // TODO: when demotion status field exists on notificationchannel
+                        mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
                         break;
 
                 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index cc5514f..d852fa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -571,7 +571,7 @@
     }
 
     private NotificationEntry createNotification() {
-        Notification.Builder n = new Notification.Builder(mContext, "")
+        Notification.Builder n = new Notification.Builder(mContext, "id")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
                 .setContentText("Text");
@@ -582,6 +582,7 @@
                 .setUid(TEST_UID)
                 .setId(mId++)
                 .setNotification(n.build())
+                .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
                 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
                 .build();
     }
@@ -616,7 +617,7 @@
     @Test
     public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        Notification.Builder n = new Notification.Builder(mContext, "")
+        Notification.Builder n = new Notification.Builder(mContext, "di")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
                 .setContentText("Text");
@@ -628,6 +629,7 @@
                 .setId(mId++)
                 .setNotification(n.build())
                 .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+                .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
                 .build();
 
         mEntryManager.addActiveNotificationForTest(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 93909dc..7c3665b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -60,7 +60,8 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -75,7 +76,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(true);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -90,7 +92,8 @@
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setNotification(notification)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -106,7 +109,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_LOW)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it has high priority
         assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -122,7 +126,8 @@
                 .setNotification(notification)
                 .setImportance(IMPORTANCE_MIN)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(false);
 
         // THEN it does NOT have high priority
         assertFalse(mHighPriorityProvider.isHighPriority(entry));
@@ -144,7 +149,8 @@
                 .setNotification(notification)
                 .setChannel(channel)
                 .build();
-        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+        when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+                .thenReturn(true);
 
         // THEN it does NOT have high priority
         assertFalse(mHighPriorityProvider.isHighPriority(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index e273191..7ab4846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection
 
 import android.app.Notification
+import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_LOW
@@ -81,6 +82,7 @@
                 .setNotification(
                         Notification.Builder(mContext, "test")
                                 .build())
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -94,6 +96,7 @@
                 .setNotification(
                         Notification.Builder(mContext, "test")
                                 .build())
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -116,6 +119,7 @@
                 .setOpPkg("pkg")
                 .setTag("tag")
                 .setNotification(aN)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -130,6 +134,7 @@
                 .setOpPkg("pkg2")
                 .setTag("tag")
                 .setNotification(bN)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
@@ -149,6 +154,7 @@
                 .setTag("tag")
                 .setNotification(notif)
                 .setUser(mContext.user)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setOverrideGroupKey("")
                 .build()
 
@@ -168,6 +174,7 @@
                 .setTag("tag")
                 .setNotification(notif)
                 .setUser(mContext.user)
+                .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
                 .setOverrideGroupKey("")
                 .build()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 0bf458c..61f0b26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -62,6 +62,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -684,6 +685,34 @@
     }
 
     @Test
+    public void testDemote() throws Exception {
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                null,
+                true);
+
+
+        ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
+        demote.performClick();
+        mTestableLooper.processAllMessages();
+
+        ArgumentCaptor<NotificationChannel> captor =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), anyInt(), captor.capture());
+        assertTrue(captor.getValue().isDemoted());
+    }
+
+    @Test
     public void testMute_mute() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7ac45f0..6f16574 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -227,6 +227,7 @@
         assertEquals(expected.getLightColor(), actual.getLightColor());
         assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
         assertEquals(expected.getConversationId(), actual.getConversationId());
+        assertEquals(expected.isDemoted(), actual.isDemoted());
     }
 
     private void compareChannelsParentChild(NotificationChannel parent,
@@ -355,6 +356,7 @@
         channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
         channel2.setLightColor(Color.BLUE);
         channel2.setConversationId("id1", "conversation");
+        channel2.setDemoted(true);
 
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);