Add Notification.Builder.setAllowSystemGeneratedContextualActions().

If an app doesn't add any smart replies or smart (contextual) actions to
their (message) notifications the Android platform fills in smart
replies and actions for the app.
With this CL we add a way to opt out of system generated contextual
actions.

Bug: 119765729
Test: atest NotificationContentViewTest
Change-Id: I6190fd197f74b8353f7113f2d8ba86068c2b78ab
diff --git a/api/current.txt b/api/current.txt
index edd37ab..4a61a4b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5229,6 +5229,7 @@
     ctor public Notification(android.os.Parcel);
     method public android.app.Notification clone();
     method public int describeContents();
+    method public boolean getAllowSystemGeneratedContextualActions();
     method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
     method public java.lang.String getChannelId();
@@ -5460,6 +5461,7 @@
     method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+    method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
     method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f2a3e44..75b56f3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
     private int mBadgeIcon = BADGE_ICON_NONE;
 
     /**
+     * Determines whether the platform can generate contextual actions for a notification.
+     */
+    private boolean mAllowSystemGeneratedContextualActions = true;
+
+    /**
      * Structure to encapsulate a named action that can be shown as part of this notification.
      * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
      * selected by the user.
@@ -2238,6 +2243,8 @@
         if (parcel.readInt() != 0) {
             mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
         }
+
+        mAllowSystemGeneratedContextualActions = parcel.readBoolean();
     }
 
     @Override
@@ -2353,6 +2360,7 @@
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
         that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2681,6 +2689,8 @@
             parcel.writeInt(0);
         }
 
+        parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
         // mUsesStandardHeader is not written because it should be recomputed in listeners
     }
 
@@ -3101,6 +3111,10 @@
         return mAppOverlayIntent;
     }
 
+    public boolean getAllowSystemGeneratedContextualActions() {
+        return mAllowSystemGeneratedContextualActions;
+    }
+
     /**
      * The small icon representing this notification in the status bar and content view.
      *
@@ -5657,6 +5671,15 @@
         }
 
         /**
+         * Determines whether the platform can generate contextual actions for a notification.
+         * By default this is true.
+         */
+        public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+            mN.mAllowSystemGeneratedContextualActions = allowed;
+            return this;
+        }
+
+        /**
          * @deprecated Use {@link #build()} instead.
          */
         @Deprecated
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index bb9a341..d4e65db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,7 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -1322,6 +1323,10 @@
         List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
         boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
 
+        List<Notification.Action> sysGeneratedSmartActions =
+                notification.getAllowSystemGeneratedContextualActions()
+                        ? entry.systemGeneratedSmartActions : Collections.emptyList();
+
         if (appGeneratedSmartRepliesExist) {
             return new SmartRepliesAndActions(remoteInputActionPair.first,
                     remoteInputActionPair.second.actionIntent,
@@ -1338,11 +1343,11 @@
             return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
                     freeformRemoteInputActionPair.second.actionIntent,
                     entry.smartReplies,
-                    entry.systemGeneratedSmartActions,
+                    sysGeneratedSmartActions,
                     freeformRemoteInputActionPair);
         }
         // App didn't generate anything, and there are no NAS-generated smart replies.
-        return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
+        return new SmartRepliesAndActions(null, null, null, sysGeneratedSmartActions,
                 freeformRemoteInputActionPair);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index a6725b8..5e137a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -106,7 +106,8 @@
         mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
 
-        // Smart replies
+        // Smart replies and actions
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
         mEntry = new NotificationData.Entry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
@@ -299,6 +300,23 @@
         assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
     }
 
+    @Test
+    public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // actions.
+        setupAppGeneratedReplies(null);
+
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(null));
+        assertThat(repliesAndActions.smartActions, is(empty()));
+    }
+
     private Notification.Action.Builder createActionBuilder(String actionTitle) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);