When a Bubble notification is clicked, show the bubble

After checking keyguard and collapsing the shade, ask
BubbleController to show the bubble associated with the
notification, instad of simply sending the contentIntent.

Bug: 123710619
Test: atest StatusBarNotificationActivityStarterTest
Change-Id: I7b43061447de0daa314deec5abad634fd73e9831
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e84c648..3aa9f73 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -293,6 +293,17 @@
     }
 
     /**
+     * Request the stack expand if needed, then select the specified Bubble as current.
+     *
+     * @param notificationKey the notification key for the bubble to be selected
+     */
+    public void expandStackAndSelectBubble(String notificationKey) {
+        if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) {
+            mStackView.setExpandedBubble(notificationKey);
+        }
+    }
+
+    /**
      * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
      */
     void dismissStack(@DismissReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index b788f53..fd2f720 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -78,8 +78,7 @@
         row.setJustClicked(true);
         DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
 
-        // If it was a bubble we should close it
-        if (row.getEntry().isBubble()) {
+        if (!row.getEntry().isBubble()) {
             mBubbleController.collapseStack();
         }
 
@@ -95,7 +94,8 @@
      */
     public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
         Notification notification = sbn.getNotification();
-        if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+        if (notification.contentIntent != null || notification.fullScreenIntent != null
+                || row.getEntry().isBubble()) {
             row.setOnClickListener(this);
         } else {
             row.setOnClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index b91cdaf..d3e5af8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -220,8 +220,6 @@
             StatusBarNotification sbn,
             ExpandableNotificationRow row) {
         row.setIsLowPriority(entry.ambient);
-        // bind the click event to the content area
-        checkNotNull(mNotificationClicker).register(row, sbn);
 
         // Extract target SDK version.
         try {
@@ -257,6 +255,9 @@
         row.setNeedsRedaction(
                 Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
         row.inflateViews();
+
+        // bind the click event to the content area
+        checkNotNull(mNotificationClicker).register(row, sbn);
     }
 
     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index db91d01..7e06232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -25,6 +25,7 @@
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 
+import static com.android.systemui.Dependency.BG_HANDLER;
 import static com.android.systemui.Dependency.MAIN_HANDLER;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -1076,7 +1077,7 @@
                 mLockscreenUserManager, shadeController, mKeyguardMonitor,
                 mNotificationInterruptionStateProvider, mMetricsLogger,
                 new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER),
-                mActivityIntentHelper);
+                Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController);
 
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 215f5c4..e4af15c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -48,6 +48,7 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -98,7 +99,9 @@
     private final CommandQueue mCommandQueue;
     private final IDreamManager mDreamManager;
     private final Handler mMainThreadHandler;
+    private final Handler mBackgroundHandler;
     private final ActivityIntentHelper mActivityIntentHelper;
+    private final BubbleController mBubbleController;
 
     private boolean mIsCollapsingToShowActivityOverLockscreen;
 
@@ -125,7 +128,9 @@
             MetricsLogger metricsLogger,
             LockPatternUtils lockPatternUtils,
             Handler mainThreadHandler,
-            ActivityIntentHelper activityIntentHelper) {
+            Handler backgroundHandler,
+            ActivityIntentHelper activityIntentHelper,
+            BubbleController bubbleController) {
         mContext = context;
         mNotificationPanel = panel;
         mPresenter = presenter;
@@ -147,6 +152,7 @@
         mAssistManager = assistManager;
         mGroupManager = groupManager;
         mLockPatternUtils = lockPatternUtils;
+        mBackgroundHandler = backgroundHandler;
         mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onPendingEntryAdded(NotificationEntry entry) {
@@ -156,6 +162,7 @@
         mStatusBarRemoteInputCallback = remoteInputCallback;
         mMainThreadHandler = mainThreadHandler;
         mActivityIntentHelper = activityIntentHelper;
+        mBubbleController = bubbleController;
     }
 
     /**
@@ -178,14 +185,24 @@
         final PendingIntent intent = notification.contentIntent != null
                 ? notification.contentIntent
                 : notification.fullScreenIntent;
+        final boolean isBubble = row.getEntry().isBubble();
+
+        // This code path is now executed for notification without a contentIntent.
+        // The only valid case is Bubble notifications. Guard against other cases
+        // entering here.
+        if (intent == null && !isBubble) {
+            Log.e(TAG, "onNotificationClicked called for non-clickable notification!");
+            return;
+        }
+
         final String notificationKey = sbn.getKey();
 
-        boolean isActivityIntent = intent.isActivity();
+        boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
         final boolean afterKeyguardGone = isActivityIntent
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
         final boolean wasOccluded = mShadeController.isOccluded();
-        boolean showOverLockscreen = mKeyguardMonitor.isShowing()
+        boolean showOverLockscreen = mKeyguardMonitor.isShowing() && intent != null
                 && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
         ActivityStarter.OnDismissAction postKeyguardAction =
@@ -244,9 +261,8 @@
             mShadeController.addAfterKeyguardGoneRunnable(runnable);
             mShadeController.collapsePanel();
         } else {
-            new Thread(runnable).start();
+            mBackgroundHandler.postAtFrontOfQueue(runnable);
         }
-
         return !mNotificationPanel.isFullyCollapsed();
     }
 
@@ -287,6 +303,7 @@
         }
         Intent fillInIntent = null;
         NotificationEntry entry = row.getEntry();
+        final boolean isBubble = entry.isBubble();
         CharSequence remoteInputText = null;
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
@@ -295,8 +312,12 @@
             fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
                     remoteInputText.toString());
         }
-        startNotificationIntent(intent, fillInIntent, row, wasOccluded, isActivityIntent);
-        if (isActivityIntent) {
+        if (isBubble) {
+            expandBubbleStackOnMainThread(notificationKey);
+        } else {
+            startNotificationIntent(intent, fillInIntent, row, wasOccluded, isActivityIntent);
+        }
+        if (isActivityIntent || isBubble) {
             mAssistManager.hideAssist();
         }
         if (shouldCollapse()) {
@@ -316,18 +337,29 @@
         } catch (RemoteException ex) {
             // system process is dead if we're here.
         }
-        if (parentToCancelFinal != null) {
-            removeNotification(parentToCancelFinal);
-        }
-        if (shouldAutoCancel(sbn)
-                || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
-                notificationKey)) {
-            // Automatically remove all notifications that we may have kept around longer
-            removeNotification(sbn);
+        if (!isBubble) {
+            if (parentToCancelFinal != null) {
+                removeNotification(parentToCancelFinal);
+            }
+            if (shouldAutoCancel(sbn)
+                    || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+                    notificationKey)) {
+                // Automatically remove all notifications that we may have kept around longer
+                removeNotification(sbn);
+            }
         }
         mIsCollapsingToShowActivityOverLockscreen = false;
     }
 
+    private void expandBubbleStackOnMainThread(String notificationKey) {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            mBubbleController.expandStackAndSelectBubble(notificationKey);
+        } else {
+            mMainThreadHandler.post(
+                    () -> mBubbleController.expandStackAndSelectBubble(notificationKey));
+        }
+    }
+
     private void startNotificationIntent(PendingIntent intent, Intent fillInIntent,
             ExpandableNotificationRow row, boolean wasOccluded, boolean isActivityIntent) {
         RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,