Merge "Temporary affordance for blocking helper"
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 20cd906..b7b2b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,6 +55,7 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 
 import java.lang.annotation.Retention;
@@ -1543,7 +1544,11 @@
             return mShowBadge;
         }
 
-        private void populate(String key, int rank, boolean matchesInterruptionFilter,
+        /**
+         * @hide
+         */
+        @VisibleForTesting
+        public void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 7f37087..4614999 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -54,6 +54,18 @@
         android:paddingStart="8dp"
         />
 
+    <ImageButton
+        android:id="@+id/helper"
+        android:layout_width="48dp"
+        android:layout_height="@*android:dimen/notification_header_height"
+        android:layout_gravity="top|end"
+        android:layout_marginEnd="6dp"
+        android:src="@drawable/ic_dnd"
+        android:tint="#FF0000"
+        android:background="@drawable/ripple_drawable"
+        android:visibility="visible"
+        />
+
     <ViewStub
         android:layout="@layout/notification_children_container"
         android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index d1e6dcc..bf8a64c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -33,6 +33,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -173,6 +174,7 @@
     private FalsingManager mFalsingManager;
     private AboveShelfChangedListener mAboveShelfChangedListener;
     private HeadsUpManager mHeadsUpManager;
+    private View mHelperButton;
 
     private boolean mJustClicked;
     private boolean mIconAnimationRunning;
@@ -387,6 +389,9 @@
         updateLimits();
         updateIconVisibilities();
         updateShelfIconColor();
+
+        showBlockingHelper(mEntry.userSentiment ==
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
 
     @VisibleForTesting
@@ -1318,6 +1323,10 @@
         requestLayout();
     }
 
+    public void showBlockingHelper(boolean show) {
+        mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -1325,6 +1334,12 @@
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
         mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
 
+        final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
+        mHelperButton = findViewById(R.id.helper);
+        mHelperButton.setOnClickListener(view -> {
+            doLongClickCallback();
+        });
+
         for (NotificationContentView l : mLayouts) {
             l.setExpandClickListener(mExpandClickListener);
             l.setContainingNotification(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index d0417b5..7e0dba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -86,6 +86,8 @@
         public RemoteViews cachedAmbientContentView;
         public CharSequence remoteInputText;
         public List<SnoozeCriterion> snoozeCriteria;
+        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
         private InflationTask mRunningTask = null;
@@ -463,6 +465,7 @@
                     }
                     entry.channel = getChannel(entry.key);
                     entry.snoozeCriteria = getSnoozeCriteria(entry.key);
+                    entry.userSentiment = mTmpRanking.getUserSentiment();
                 }
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 0a68389..f9ec3f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -32,11 +32,14 @@
 
 import android.app.ActivityManager;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -120,6 +123,23 @@
         }
     }
 
+    private void setUserSentiment(String key, int sentiment) {
+        doAnswer(invocationOnMock -> {
+            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+                    invocationOnMock.getArguments()[1];
+            ranking.populate(
+                    key,
+                    0,
+                    false,
+                    0,
+                    0,
+                    NotificationManager.IMPORTANCE_DEFAULT,
+                    null, null,
+                    null, null, null, true, sentiment);
+            return true;
+        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -158,6 +178,8 @@
 
         mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+
+        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
 
     @Test
@@ -196,6 +218,8 @@
 
         assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
         assertNotNull(entry.row);
+        assertEquals(mEntry.userSentiment,
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
 
     @Test
@@ -204,6 +228,8 @@
 
         mEntryManager.getNotificationData().add(mEntry);
 
+        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
         mHandler.post(() -> {
             mEntryManager.updateNotification(mSbn, mRankingMap);
         });
@@ -219,6 +245,8 @@
         verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
         verify(mCallback).onNotificationUpdated(mSbn);
         assertNotNull(mEntry.row);
+        assertEquals(mEntry.userSentiment,
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
 
     @Test