Auto expand bubbles when foreground with the flag set

- Tests if you're not foreground that it's not auto expanded
- Tests if you are foreground it does expand

Test: atest BubbleControllerTest
Bug: 123542531
Change-Id: I016a39236f1d1ac83518f6cc57099c4e5bc83686
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 4a2731e..0b12ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -26,6 +26,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
@@ -64,6 +65,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.lang.annotation.Retention;
+import java.util.List;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -340,6 +342,9 @@
             // It's new
             mStackView.addBubble(notif);
         }
+        if (shouldAutoExpand(notif)) {
+            mStackView.setExpandedBubble(notif);
+        }
         updateVisibility();
     }
 
@@ -522,6 +527,23 @@
                 || autoBubbleAll;
     }
 
+    private boolean shouldAutoExpand(NotificationEntry entry) {
+        Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
+        return metadata != null && metadata.getAutoExpandBubble()
+                && isForegroundApp(entry.notification.getPackageName());
+    }
+
+    /**
+     * Return true if the applications with the package name is running in foreground.
+     *
+     * @param pkgName application package name.
+     */
+    private boolean isForegroundApp(String pkgName) {
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        List<RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
+        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+    }
+
     /**
      * This task stack listener is responsible for responding to tasks moved to the front
      * which are on the default (main) display. When this happens, expanded bubbles must be
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index c9b550c..f666d60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bubbles;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -26,8 +28,13 @@
 import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
+import android.app.Notification;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
@@ -36,6 +43,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -55,6 +63,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -83,6 +94,7 @@
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
     private ExpandableNotificationRow mNoChannelRow;
+    private ExpandableNotificationRow mAutoExpandRow;
 
     @Mock
     private NotificationData mNotificationData;
@@ -104,7 +116,6 @@
         mStatusBarView = new FrameLayout(mContext);
         mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
 
-
         // Bubbles get added to status bar window view
         mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
                 mActivityManager, mDozeParameters);
@@ -115,6 +126,9 @@
         mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
         mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
         mNoChannelRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+        Notification.BubbleMetadata metadata = getBuilder().setAutoExpandBubble(true).build();
+        mAutoExpandRow = mNotificationTestHelper.createBubble(metadata,
+                "com.android.systemui.tests");
 
         // Return non-null notification data from the NEM
         when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
@@ -303,6 +317,63 @@
     }
 
     @Test
+    public void testAutoExpandFailsNotForeground() {
+        assertFalse(mBubbleController.isStackExpanded());
+
+        // Add the auto expand bubble
+        mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry());
+        mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */);
+
+        // Expansion shouldn't change
+        verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
+                mAutoExpandRow.getEntry().key);
+        assertFalse(mBubbleController.isStackExpanded());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+    }
+
+    @Test
+    public void testAutoExpandSucceedsForeground() {
+        final CountDownLatch latch = new CountDownLatch(1);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                latch.countDown();
+            }
+        };
+        IntentFilter filter = new IntentFilter(BubblesTestActivity.BUBBLE_ACTIVITY_OPENED);
+        mContext.registerReceiver(receiver, filter);
+
+        assertFalse(mBubbleController.isStackExpanded());
+
+        // Make ourselves foreground
+        Intent i = new Intent(mContext, BubblesTestActivity.class);
+        i.setFlags(FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(i);
+
+        try {
+            latch.await(100, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        // Add the auto expand bubble
+        mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry());
+        mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */);
+
+        // Expansion should change
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
+                mAutoExpandRow.getEntry().key);
+        assertTrue(mBubbleController.isStackExpanded());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+        mContext.unregisterReceiver(receiver);
+    }
+
+
+    @Test
     public void testMarkNewNotificationAsBubble() {
         mEntryListener.onPendingEntryAdded(mRow.getEntry());
         assertTrue(mRow.getEntry().isBubble());
@@ -349,4 +420,15 @@
             return entry.notification.getNotification().getBubbleMetadata() != null;
         }
     }
+
+    /**
+     * @return basic {@link android.app.Notification.BubbleMetadata.Builder}
+     */
+    private Notification.BubbleMetadata.Builder getBuilder() {
+        Intent target = new Intent(mContext, BubblesTestActivity.class);
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
+        return new Notification.BubbleMetadata.Builder()
+                .setIntent(bubbleIntent)
+                .setIcon(Icon.createWithResource(mContext, R.drawable.android));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
index ea472da..43d2ad1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -17,6 +17,7 @@
 package com.android.systemui.bubbles;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.os.Bundle;
 
 import com.android.systemui.R;
@@ -26,9 +27,15 @@
  */
 public class BubblesTestActivity extends Activity {
 
+    public static final String BUBBLE_ACTIVITY_OPENED =
+            "com.android.systemui.bubbles.BUBBLE_ACTIVITY_OPENED";
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
+
+        Intent i = new Intent(BUBBLE_ACTIVITY_OPENED);
+        sendBroadcast(i);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 1bcf880..8c5f6f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -151,24 +151,41 @@
 
     /**
      * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
-     */
-    public ExpandableNotificationRow createBubble() throws Exception {
-        return createBubble(null);
-    }
-
-    /**
-     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
      *
      * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent}
      */
     public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent)
             throws Exception {
         Notification n = createNotification(false /* isGroupSummary */,
-                null /* groupKey */, true /* isBubble */, deleteIntent);
+                null /* groupKey */, makeBubbleMetadata(deleteIntent));
         return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
     }
 
     /**
+     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+     *
+     * @param bubbleMetadata the {@link BubbleMetadata} to use
+     */
+    public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata)
+            throws Exception {
+        Notification n = createNotification(false /* isGroupSummary */,
+                null /* groupKey */, bubbleMetadata);
+        return generateRow(n, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+     *
+     * @param bubbleMetadata the {@link BubbleMetadata} to use
+     */
+    public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg)
+            throws Exception {
+        Notification n = createNotification(false /* isGroupSummary */,
+                null /* groupKey */, bubbleMetadata);
+        return generateRow(n, pkg, UID, USER_HANDLE, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
+    }
+
+    /**
      * Creates a notification row with the given details.
      *
      * @param pkg package used for creating a {@link StatusBarNotification}
@@ -207,8 +224,7 @@
      * @return a notification that is in the group specified or standalone if unspecified
      */
     private Notification createNotification(boolean isGroupSummary, @Nullable String groupKey) {
-        return createNotification(isGroupSummary, groupKey, false /* isBubble */,
-                null /* bubbleDeleteIntent */);
+        return createNotification(isGroupSummary, groupKey, null /* bubble metadata */);
     }
 
     /**
@@ -216,12 +232,11 @@
      *
      * @param isGroupSummary whether the notification is a group summary
      * @param groupKey the group key for the notification group used across notifications
-     * @param isBubble whether this notification should bubble
+     * @param bubbleMetadata the bubble metadata to use for this notification if it exists.
      * @return a notification that is in the group specified or standalone if unspecified
      */
     private Notification createNotification(boolean isGroupSummary,
-            @Nullable String groupKey, boolean isBubble,
-            @Nullable PendingIntent bubbleDeleteIntent) {
+            @Nullable String groupKey, @Nullable BubbleMetadata bubbleMetadata) {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -239,9 +254,8 @@
         if (!TextUtils.isEmpty(groupKey)) {
             notificationBuilder.setGroup(groupKey);
         }
-        if (isBubble) {
-            BubbleMetadata metadata = makeBubbleMetadata(bubbleDeleteIntent);
-            notificationBuilder.setBubbleMetadata(metadata);
+        if (bubbleMetadata != null) {
+            notificationBuilder.setBubbleMetadata(bubbleMetadata);
         }
         return notificationBuilder.build();
     }