From 8a52dc3af4069e51f5b40553c2b027781d678963 Mon Sep 17 00:00:00 2001 From: Gustav Sennton Date: Mon, 15 Apr 2019 12:48:23 +0100 Subject: [PATCH] Block clicks on smart actions and replies just after creation/update. To avoid accidental clicks on smart actions and replies we here block clicks on those buttons just after they are created. We block clicks on those buttons when a notification is updated - but only if the buttons are new, or different from previous buttons shown in the notification. I.e. if the notification is updated but the smart suggestion buttons stay the same we don't block clicks on them. Bug: 128683184 Test: manually ensure clicks are blocked within the initialization delay (for new / changed buttons), and ensure the delay changes when calling adb shell device_config put systemui ssin_onclick_init_delay X where X is the delay in ms. Test: SmartReplyConstantsTest, SmartReplyViewTest Change-Id: I9a44eb6ade6579a42e35b36cce4bd5863332c60e --- .../sysui/SystemUiDeviceConfigFlags.java | 6 + packages/SystemUI/res/values/config.xml | 4 + .../row/ExpandableNotificationRow.java | 8 ++ .../row/NotificationContentInflater.java | 13 +- .../row/NotificationContentView.java | 20 ++- .../policy/InflatedSmartReplies.java | 55 ++++++-- .../statusbar/policy/SmartReplyConstants.java | 16 +++ .../statusbar/policy/SmartReplyView.java | 60 ++++++-- .../policy/BlockingQueueIntentReceiver.java | 4 + .../policy/InflatedSmartRepliesTest.java | 69 ++++++++++ .../policy/SmartReplyConstantsTest.java | 14 ++ .../statusbar/policy/SmartReplyViewTest.java | 129 ++++++++++++++++-- 12 files changed, 352 insertions(+), 46 deletions(-) diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 495a5fbb66657..6d0a8646b3a77 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -81,6 +81,12 @@ public final class SystemUiDeviceConfigFlags { */ public static final String SSIN_MAX_NUM_ACTIONS = "ssin_max_num_actions"; + /** + * (int) The amount of time (ms) before smart suggestions are clickable, since the suggestions + * were added. + */ + public static final String SSIN_ONCLICK_INIT_DELAY = "ssin_onclick_init_delay"; + /** * The default component of * {@link android.service.notification.NotificationAssistantService}. diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index e02be38d32e95..99a75d228f57a 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -469,6 +469,10 @@ --> -1 + + 200 + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index efdcd053bc54c..6b2efaab0a644 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -100,6 +100,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -3194,6 +3195,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mAmbientGoingAway = goingAway; } + /** + * Returns the Smart Suggestions backing the smart suggestion buttons in the notification. + */ + public SmartRepliesAndActions getExistingSmartRepliesAndActions() { + return mPrivateLayout.getCurrentSmartRepliesAndActions(); + } + @VisibleForTesting protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) { mChildrenContainer = childrenContainer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 09f513dc15790..f095b907e8ee3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -44,6 +44,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.util.Assert; @@ -284,7 +285,8 @@ public class NotificationContentInflater { mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(), - mRow.getContext(), mRow.getHeadsUpManager()); + mRow.getContext(), mRow.getHeadsUpManager(), + mRow.getExistingSmartRepliesAndActions()); apply( inflateSynchronously, result, @@ -346,20 +348,20 @@ public class NotificationContentInflater { private static InflationProgress inflateSmartReplyViews(InflationProgress result, @InflationFlag int reInflateFlags, NotificationEntry entry, Context context, - HeadsUpManager headsUpManager) { + HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) { SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class); SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class); if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) { result.expandedInflatedSmartReplies = InflatedSmartReplies.inflate( context, entry, smartReplyConstants, smartReplyController, - headsUpManager); + headsUpManager, previousSmartRepliesAndActions); } if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) { result.headsUpInflatedSmartReplies = InflatedSmartReplies.inflate( context, entry, smartReplyConstants, smartReplyController, - headsUpManager); + headsUpManager, previousSmartRepliesAndActions); } return result; } @@ -907,7 +909,8 @@ public class NotificationContentInflater { mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(), - mRow.getContext(), mRow.getHeadsUpManager()); + mRow.getContext(), mRow.getHeadsUpManager(), + mRow.getExistingSmartRepliesAndActions()); } catch (Exception e) { mError = e; return null; 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 78500357f41f3..b81d81438ea39 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 @@ -93,6 +93,7 @@ public class NotificationContentView extends FrameLayout { private SmartReplyController mSmartReplyController; private InflatedSmartReplies mExpandedInflatedSmartReplies; private InflatedSmartReplies mHeadsUpInflatedSmartReplies; + private SmartRepliesAndActions mCurrentSmartRepliesAndActions; private NotificationViewWrapper mContractedWrapper; private NotificationViewWrapper mExpandedWrapper; @@ -1259,18 +1260,18 @@ public class NotificationContentView extends FrameLayout { // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between // this class and the InflatedSmartReplies classes. - SmartRepliesAndActions smartRepliesAndActions = mExpandedInflatedSmartReplies != null + mCurrentSmartRepliesAndActions = mExpandedInflatedSmartReplies != null ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions() : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions(); if (DEBUG) { Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.", entry.notification.getKey(), - smartRepliesAndActions.smartActions == null ? 0 : - smartRepliesAndActions.smartActions.actions.size(), - smartRepliesAndActions.smartReplies == null ? 0 : - smartRepliesAndActions.smartReplies.choices.length)); + mCurrentSmartRepliesAndActions.smartActions == null ? 0 : + mCurrentSmartRepliesAndActions.smartActions.actions.size(), + mCurrentSmartRepliesAndActions.smartReplies == null ? 0 : + mCurrentSmartRepliesAndActions.smartReplies.choices.length)); } - applySmartReplyView(smartRepliesAndActions, entry); + applySmartReplyView(mCurrentSmartRepliesAndActions, entry); } private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) { @@ -1472,6 +1473,13 @@ public class NotificationContentView extends FrameLayout { } } + /** + * Returns the smart replies and actions currently shown in the notification. + */ + @Nullable public SmartRepliesAndActions getCurrentSmartRepliesAndActions() { + return mCurrentSmartRepliesAndActions; + } + public void closeRemoteInput() { if (mHeadsUpRemoteInput != null) { mHeadsUpRemoteInput.close(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java index 5b2e398b66e14..ee78a723a49c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java @@ -28,15 +28,19 @@ import android.util.Log; import android.util.Pair; import android.widget.Button; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.systemui.Dependency; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper; +import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -79,29 +83,52 @@ public class InflatedSmartReplies { NotificationEntry entry, SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController, - HeadsUpManager headsUpManager) { - SmartRepliesAndActions smartRepliesAndActions = + HeadsUpManager headsUpManager, + SmartRepliesAndActions existingSmartRepliesAndActions) { + SmartRepliesAndActions newSmartRepliesAndActions = chooseSmartRepliesAndActions(smartReplyConstants, entry); - if (!shouldShowSmartReplyView(entry, smartRepliesAndActions)) { + if (!shouldShowSmartReplyView(entry, newSmartRepliesAndActions)) { return new InflatedSmartReplies(null /* smartReplyView */, - null /* smartSuggestionButtons */, smartRepliesAndActions); + null /* smartSuggestionButtons */, newSmartRepliesAndActions); } + // Only block clicks if the smart buttons are different from the previous set - to avoid + // scenarios where a user incorrectly cannot click smart buttons because the notification is + // updated. + boolean delayOnClickListener = + !areSuggestionsSimilar(existingSmartRepliesAndActions, newSmartRepliesAndActions); + SmartReplyView smartReplyView = SmartReplyView.inflate(context); List