Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +22 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.notification.NotificationAlertingMa 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; Loading Loading @@ -64,6 +65,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import java.lang.annotation.Retention; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; Loading Loading @@ -340,6 +342,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe // It's new mStackView.addBubble(notif); } if (shouldAutoExpand(notif)) { mStackView.setExpandedBubble(notif); } updateVisibility(); } Loading Loading @@ -522,6 +527,23 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe || 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 Loading packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +83 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -26,8 +28,13 @@ import static org.mockito.Mockito.verify; 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; Loading @@ -36,6 +43,7 @@ import android.widget.FrameLayout; 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; Loading @@ -55,6 +63,9 @@ import org.mockito.Captor; 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) Loading Loading @@ -83,6 +94,7 @@ public class BubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mNoChannelRow; private ExpandableNotificationRow mAutoExpandRow; @Mock private NotificationData mNotificationData; Loading @@ -104,7 +116,6 @@ public class BubbleControllerTest extends SysuiTestCase { mStatusBarView = new FrameLayout(mContext); mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager); // Bubbles get added to status bar window view mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager, mActivityManager, mDozeParameters); Loading @@ -115,6 +126,9 @@ public class BubbleControllerTest extends SysuiTestCase { 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); Loading Loading @@ -302,6 +316,63 @@ public class BubbleControllerTest extends SysuiTestCase { assertFalse(mBubbleController.hasBubbles()); } @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()); Loading Loading @@ -349,4 +420,15 @@ public class BubbleControllerTest extends SysuiTestCase { 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)); } } packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -26,9 +27,15 @@ import com.android.systemui.R; */ 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); } } packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +27 −13 Original line number Diff line number Diff line Loading @@ -151,23 +151,40 @@ public class NotificationTestHelper { /** * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. * * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent} */ public ExpandableNotificationRow createBubble() throws Exception { return createBubble(null); public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent) throws Exception { Notification n = createNotification(false /* isGroupSummary */, 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 deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent} * @param bubbleMetadata the {@link BubbleMetadata} to use */ public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent) public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata) throws Exception { Notification n = createNotification(false /* isGroupSummary */, null /* groupKey */, true /* isBubble */, deleteIntent); 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. * Loading Loading @@ -207,8 +224,7 @@ public class NotificationTestHelper { * @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 */); } /** Loading @@ -216,12 +232,11 @@ public class NotificationTestHelper { * * @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(), Loading @@ -239,9 +254,8 @@ public class NotificationTestHelper { 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(); } Loading Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +22 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.notification.NotificationAlertingMa 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; Loading Loading @@ -64,6 +65,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import java.lang.annotation.Retention; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; Loading Loading @@ -340,6 +342,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe // It's new mStackView.addBubble(notif); } if (shouldAutoExpand(notif)) { mStackView.setExpandedBubble(notif); } updateVisibility(); } Loading Loading @@ -522,6 +527,23 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe || 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 Loading
packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +83 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -26,8 +28,13 @@ import static org.mockito.Mockito.verify; 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; Loading @@ -36,6 +43,7 @@ import android.widget.FrameLayout; 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; Loading @@ -55,6 +63,9 @@ import org.mockito.Captor; 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) Loading Loading @@ -83,6 +94,7 @@ public class BubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mNoChannelRow; private ExpandableNotificationRow mAutoExpandRow; @Mock private NotificationData mNotificationData; Loading @@ -104,7 +116,6 @@ public class BubbleControllerTest extends SysuiTestCase { mStatusBarView = new FrameLayout(mContext); mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager); // Bubbles get added to status bar window view mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager, mActivityManager, mDozeParameters); Loading @@ -115,6 +126,9 @@ public class BubbleControllerTest extends SysuiTestCase { 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); Loading Loading @@ -302,6 +316,63 @@ public class BubbleControllerTest extends SysuiTestCase { assertFalse(mBubbleController.hasBubbles()); } @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()); Loading Loading @@ -349,4 +420,15 @@ public class BubbleControllerTest extends SysuiTestCase { 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)); } }
packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -26,9 +27,15 @@ import com.android.systemui.R; */ 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); } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +27 −13 Original line number Diff line number Diff line Loading @@ -151,23 +151,40 @@ public class NotificationTestHelper { /** * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. * * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent} */ public ExpandableNotificationRow createBubble() throws Exception { return createBubble(null); public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent) throws Exception { Notification n = createNotification(false /* isGroupSummary */, 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 deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent} * @param bubbleMetadata the {@link BubbleMetadata} to use */ public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent) public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata) throws Exception { Notification n = createNotification(false /* isGroupSummary */, null /* groupKey */, true /* isBubble */, deleteIntent); 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. * Loading Loading @@ -207,8 +224,7 @@ public class NotificationTestHelper { * @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 */); } /** Loading @@ -216,12 +232,11 @@ public class NotificationTestHelper { * * @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(), Loading @@ -239,9 +254,8 @@ public class NotificationTestHelper { 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(); } Loading