Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4991e60 authored by Mady Mellor's avatar Mady Mellor
Browse files

Enable 'real bubbles' and fix our tests

Previously the test was marking entries as 'isBubble' automatically which
isn't quite correct.

This CL fixes that and has the side effect that enables bubbles for real.
This doesn't auto-bubble any notification, however, if the notification
is on an appropriately noisy channel & has an app overlay intent we will
bubble it.

Bug: 111236845
Test: atest BubbleControllerTest
Change-Id: I26c67ce002eec808591d7864b2366bb4140be386
parent fb922e9b
Loading
Loading
Loading
Loading
+35 −5
Original line number Diff line number Diff line
@@ -22,12 +22,16 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;

import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -35,8 +39,6 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -88,6 +90,8 @@ public class BubbleController {
    // Bubbles get added to the status bar view
    private final StatusBarWindowController mStatusBarWindowController;

    private INotificationManager mNotificationManagerService;

    // Used for determining view rect for touch interaction
    private Rect mTempRect = new Rect();

@@ -124,6 +128,13 @@ public class BubbleController {
        mStatusBarWindowController = statusBarWindowController;

        mNotificationEntryManager.addNotificationEntryListener(mEntryListener);

        try {
            mNotificationManagerService = INotificationManager.Stub.asInterface(
                    ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
        } catch (ServiceManager.ServiceNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
@@ -268,7 +279,7 @@ public class BubbleController {
    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
        @Override
        public void onPendingEntryAdded(NotificationEntry entry) {
            if (shouldAutoBubble(mContext, entry)) {
            if (shouldAutoBubble(mContext, entry) || shouldBubble(entry)) {
                entry.setIsBubble(true);
            }
        }
@@ -367,19 +378,38 @@ public class BubbleController {
        return new Point(EDGE_OVERLAP, size);
    }

    /**
     * Whether the notification has been developer configured to bubble and is allowed by the user.
     */
    private boolean shouldBubble(NotificationEntry entry) {
        StatusBarNotification n = entry.notification;
        boolean canAppOverlay = false;
        try {
            canAppOverlay = mNotificationManagerService.areAppOverlaysAllowedForPackage(
                    n.getPackageName(), n.getUid());
        } catch (RemoteException e) {
            Log.w(TAG, "Error calling NoMan to determine if app can overlay", e);
        }

        boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel(
                entry.key).canOverlayApps();
        boolean hasOverlayIntent = n.getNotification().getAppOverlayIntent() != null;
        return hasOverlayIntent && canChannelOverlay && canAppOverlay;
    }

    /**
     * Whether the notification should bubble or not.
     */
    private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
    private boolean shouldAutoBubble(Context context, NotificationEntry entry) {
        if (entry.isBubbleDismissed()) {
            return false;
        }
        StatusBarNotification n = entry.notification;

        boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
        boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
        boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;

        StatusBarNotification n = entry.notification;
        boolean hasRemoteInput = false;
        if (n.getNotification().actions != null) {
            for (Notification.Action action : n.getNotification().actions) {
+3 −6
Original line number Diff line number Diff line
@@ -74,7 +74,8 @@ public class BubbleControllerTest extends SysuiTestCase {
    private ExpandableNotificationRow mRow;
    private ExpandableNotificationRow mRow2;

    private final NotificationData mNotificationData = new NotificationData();
    @Mock
    private NotificationData mNotificationData;

    @Before
    public void setUp() throws Exception {
@@ -93,6 +94,7 @@ public class BubbleControllerTest extends SysuiTestCase {

        // Return non-null notification data from the NEM
        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);

        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);

@@ -102,11 +104,6 @@ public class BubbleControllerTest extends SysuiTestCase {
        mEntryListener = mEntryListenerCaptor.getValue();
    }

    @Test
    public void testIsBubble() {
        assertTrue(mRow.getEntry().isBubble());
    }

    @Test
    public void testAddBubble() {
        mBubbleController.addBubble(mRow.getEntry());
+48 −23
Original line number Diff line number Diff line
@@ -17,13 +17,16 @@
package com.android.systemui.statusbar;

import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
@@ -86,8 +89,7 @@ public class NotificationTestHelper {
     * @throws Exception
     */
    public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */,
                false /* isBubble */);
        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
    }

    /**
@@ -98,8 +100,7 @@ public class NotificationTestHelper {
     * @throws Exception
     */
    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */,
                false /* isBubble */);
        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
    }

    /**
@@ -112,8 +113,7 @@ public class NotificationTestHelper {
     */
    public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
            throws Exception {
        return generateRow(createNotification(), PKG, UID, extraInflationFlags,
                false /* isBubble */);
        return generateRow(createNotification(), PKG, UID, extraInflationFlags);
    }

    /**
@@ -134,20 +134,21 @@ public class NotificationTestHelper {
        return createGroup(2);
    }

    /**
     * Retursn an {@link ExpandableNotificationRow} that should be a bubble.
     */
    public ExpandableNotificationRow createBubble() throws Exception {
        return createRow(PKG, UID, false /* isGroupSummary */, null /* groupKey */,
                true /* isBubble */);
    }

    private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
        return createRow(PKG, UID, true /* isGroupSummary */, groupkey, false);
        return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
    }

    private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
        return createRow(PKG, UID, false /* isGroupSummary */, groupkey, false);
        return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
    }

    /**
     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
     */
    public ExpandableNotificationRow createBubble() throws Exception {
        Notification n = createNotification(false /* isGroupSummary */,
                null /* groupKey */, true /* isBubble */);
        return generateRow(n, PKG, UID, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
    }

    /**
@@ -157,7 +158,6 @@ public class NotificationTestHelper {
     * @param uid uid used for creating a {@link StatusBarNotification}
     * @param isGroupSummary whether the notification row is a group summary
     * @param groupKey the group key for the notification group used across notifications
     * @param isBubble
     * @return a row with that's either a standalone notification or a group notification if the
     *         groupKey is non-null
     * @throws Exception
@@ -166,10 +166,10 @@ public class NotificationTestHelper {
            String pkg,
            int uid,
            boolean isGroupSummary,
            @Nullable String groupKey, boolean isBubble)
            @Nullable String groupKey)
            throws Exception {
        Notification notif = createNotification(isGroupSummary, groupKey);
        return generateRow(notif, pkg, uid, 0 /* inflationFlags */, isBubble);
        return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
    }

    /**
@@ -188,8 +188,20 @@ public class NotificationTestHelper {
     * @param groupKey the group key for the notification group used across notifications
     * @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 */);
    }

    /**
     * Creates a notification with the given parameters.
     *
     * @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
     * @return a notification that is in the group specified or standalone if unspecified
     */
    private Notification createNotification(boolean isGroupSummary,
            @Nullable String groupKey) {
            @Nullable String groupKey, boolean isBubble) {
        Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                R.drawable.ic_person)
                .setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -207,6 +219,10 @@ public class NotificationTestHelper {
        if (!TextUtils.isEmpty(groupKey)) {
            notificationBuilder.setGroup(groupKey);
        }
        if (isBubble) {
            PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
            notificationBuilder.setAppOverlayIntent(bubbleIntent);
        }
        return notificationBuilder.build();
    }

@@ -214,7 +230,17 @@ public class NotificationTestHelper {
            Notification notification,
            String pkg,
            int uid,
            @InflationFlag int extraInflationFlags, boolean isBubble)
            @InflationFlag int extraInflationFlags)
            throws Exception {
        return generateRow(notification, pkg, uid, extraInflationFlags, IMPORTANCE_DEFAULT);
    }

    private ExpandableNotificationRow generateRow(
            Notification notification,
            String pkg,
            int uid,
            @InflationFlag int extraInflationFlags,
            int importance)
            throws Exception {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                mContext.LAYOUT_INFLATER_SERVICE);
@@ -242,9 +268,8 @@ public class NotificationTestHelper {
        entry.setRow(row);
        entry.createIcons(mContext, sbn);
        entry.channel = new NotificationChannel(
                notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
                notification.getChannelId(), notification.getChannelId(), importance);
        entry.channel.setBlockableSystem(true);
        entry.setIsBubble(isBubble);
        row.setEntry(entry);
        row.getNotificationInflater().addInflationFlags(extraInflationFlags);
        NotificationInflaterTest.runThenWaitForInflation(
+2 −2
Original line number Diff line number Diff line
@@ -2313,8 +2313,8 @@ public class NotificationManagerService extends SystemService {

        @Override
        public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
            checkCallerIsSystemOrSameApp(pkg);

            enforceSystemOrSystemUIOrSamePackage("Caller not system or systemui or same package",
                    pkg);
            return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
        }