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

Commit 64e7d5c1 authored by Mady Mellor's avatar Mady Mellor
Browse files

Modify the showAppBubble method to be showOrHideAppBubble

Depending on the state of the app bubble (if it already exists),
calling this method might do different things:
- If the app bubble already exists & isn't open, open it
- If the app bubble already exists & is open, close it
- If the app bubble doesn't exist, add it & open it

Bug: 253320821
Test: atest BubblesTest
Change-Id: I707af3b46d84c97d8c864cab33dfe7d8e49921c7
parent bc2a6556
Loading
Loading
Loading
Loading
+48 −10
Original line number Original line Diff line number Diff line
@@ -973,22 +973,60 @@ public class BubbleController implements ConfigurationChangeListener {
    }
    }


    /**
    /**
     * Adds and expands bubble for a specific intent. These bubbles are <b>not</b> backed by a n
     * This method has different behavior depending on:
     * otification and remain until the user dismisses the bubble or bubble stack. Only one intent
     *    - if an app bubble exists
     * bubble is supported at a time.
     *    - if an app bubble is expanded
     *
     * If no app bubble exists, this will add and expand a bubble with the provided intent. The
     * intent must be explicit (i.e. include a package name or fully qualified component class name)
     * and the activity for it should be resizable.
     *
     * If an app bubble exists, this will toggle the visibility of it, i.e. if the app bubble is
     * expanded, calling this method will collapse it. If the app bubble is not expanded, calling
     * this method will expand it.
     *
     * These bubbles are <b>not</b> backed by a notification and remain until the user dismisses
     * the bubble or bubble stack.
     *
     * Some notes:
     *    - Only one app bubble is supported at a time
     *    - Calling this method with a different intent than the existing app bubble will do nothing
     *
     *
     * @param intent the intent to display in the bubble expanded view.
     * @param intent the intent to display in the bubble expanded view.
     */
     */
    public void showAppBubble(Intent intent) {
    public void showOrHideAppBubble(Intent intent) {
        if (intent == null || intent.getPackage() == null) return;
        if (intent == null || intent.getPackage() == null) {
            Log.w(TAG, "App bubble failed to show, invalid intent: " + intent
                    + ((intent != null) ? " with package: " + intent.getPackage() : " "));
            return;
        }


        PackageManager packageManager = getPackageManagerForUser(mContext, mCurrentUserId);
        PackageManager packageManager = getPackageManagerForUser(mContext, mCurrentUserId);
        if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;
        if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;


        Bubble existingAppBubble = mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE);
        if (existingAppBubble != null) {
            BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
            if (isStackExpanded()) {
                if (selectedBubble != null && KEY_APP_BUBBLE.equals(selectedBubble.getKey())) {
                    // App bubble is expanded, lets collapse
                    collapseStack();
                } else {
                    // App bubble is not selected, select it
                    mBubbleData.setSelectedBubble(existingAppBubble);
                }
            } else {
                // App bubble is not selected, select it & expand
                mBubbleData.setSelectedBubble(existingAppBubble);
                mBubbleData.setExpanded(true);
            }
        } else {
            // App bubble does not exist, lets add and expand it
            Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
            Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
            b.setShouldAutoExpand(true);
            b.setShouldAutoExpand(true);
            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
        }
        }
    }


    /**
    /**
     * Fills the overflow bubbles by loading them from disk.
     * Fills the overflow bubbles by loading them from disk.
@@ -1697,9 +1735,9 @@ public class BubbleController implements ConfigurationChangeListener {
        }
        }


        @Override
        @Override
        public void showAppBubble(Intent intent) {
        public void showOrHideAppBubble(Intent intent) {
            mMainExecutor.execute(() -> {
            mMainExecutor.execute(() -> {
                BubbleController.this.showAppBubble(intent);
                BubbleController.this.showOrHideAppBubble(intent);
            });
            });
        }
        }


+20 −5
Original line number Original line Diff line number Diff line
@@ -109,13 +109,28 @@ public interface Bubbles {
    void expandStackAndSelectBubble(Bubble bubble);
    void expandStackAndSelectBubble(Bubble bubble);


    /**
    /**
     * Adds and expands bubble that is not notification based, but instead based on an intent from
     * This method has different behavior depending on:
     * the app. The intent must be explicit (i.e. include a package name or fully qualified
     *    - if an app bubble exists
     * component class name) and the activity for it should be resizable.
     *    - if an app bubble is expanded
     *
     *
     * @param intent the intent to populate the bubble.
     * If no app bubble exists, this will add and expand a bubble with the provided intent. The
     * intent must be explicit (i.e. include a package name or fully qualified component class name)
     * and the activity for it should be resizable.
     *
     * If an app bubble exists, this will toggle the visibility of it, i.e. if the app bubble is
     * expanded, calling this method will collapse it. If the app bubble is not expanded, calling
     * this method will expand it.
     *
     * These bubbles are <b>not</b> backed by a notification and remain until the user dismisses
     * the bubble or bubble stack.
     *
     * Some notes:
     *    - Only one app bubble is supported at a time
     *    - Calling this method with a different intent than the existing app bubble will do nothing
     *
     * @param intent the intent to display in the bubble expanded view.
     */
     */
    void showAppBubble(Intent intent);
    void showOrHideAppBubble(Intent intent);


    /**
    /**
     * @return a bubble that matches the provided shortcutId, if one exists.
     * @return a bubble that matches the provided shortcutId, if one exists.
+4 −2
Original line number Original line Diff line number Diff line
@@ -57,7 +57,9 @@ constructor(
     * If the keyguard is locked, notes will open as a full screen experience. A locked device has
     * If the keyguard is locked, notes will open as a full screen experience. A locked device has
     * no contextual information which let us use the whole screen space available.
     * no contextual information which let us use the whole screen space available.
     *
     *
     * If no in multi-window or the keyguard is unlocked, notes will open as a floating experience.
     * If no in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
     * collapsed if the notes bubble is already opened.
     *
     * That will let users open other apps in full screen, and take contextual notes.
     * That will let users open other apps in full screen, and take contextual notes.
     */
     */
    fun showNoteTask(isInMultiWindowMode: Boolean = false) {
    fun showNoteTask(isInMultiWindowMode: Boolean = false) {
@@ -75,7 +77,7 @@ constructor(
            context.startActivity(intent)
            context.startActivity(intent)
        } else {
        } else {
            // TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
            // TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
            bubbles.showAppBubble(intent)
            bubbles.showOrHideAppBubble(intent)
        }
        }
    }
    }


+9 −9
Original line number Original line Diff line number Diff line
@@ -93,7 +93,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context).startActivity(notesIntent)
        verify(context).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -102,7 +102,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {


        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(bubbles).showAppBubble(notesIntent)
        verify(bubbles).showOrHideAppBubble(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
    }
    }


@@ -113,7 +113,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = true)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = true)


        verify(context).startActivity(notesIntent)
        verify(context).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -123,7 +123,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -133,7 +133,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -143,7 +143,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -153,7 +153,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -161,7 +161,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController(isEnabled = false).showNoteTask()
        createNoteTaskController(isEnabled = false).showNoteTask()


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }


    @Test
    @Test
@@ -171,7 +171,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)


        verify(context, never()).startActivity(notesIntent)
        verify(context, never()).startActivity(notesIntent)
        verify(bubbles, never()).showAppBubble(notesIntent)
        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
    }
    }
    // endregion
    // endregion


+62 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.service.notification.NotificationListenerService.REASON_AP
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;


import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;


import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


@@ -227,6 +228,8 @@ public class BubblesTest extends SysuiTestCase {
    private BubbleEntry mBubbleEntryUser11;
    private BubbleEntry mBubbleEntryUser11;
    private BubbleEntry mBubbleEntry2User11;
    private BubbleEntry mBubbleEntry2User11;


    private Intent mAppBubbleIntent;

    @Mock
    @Mock
    private ShellInit mShellInit;
    private ShellInit mShellInit;
    @Mock
    @Mock
@@ -319,6 +322,9 @@ public class BubblesTest extends SysuiTestCase {
        mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
        mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
                mNotificationTestHelper.createBubble(handle));
                mNotificationTestHelper.createBubble(handle));


        mAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
        mAppBubbleIntent.setPackage(mContext.getPackageName());

        mZenModeConfig.suppressedVisualEffects = 0;
        mZenModeConfig.suppressedVisualEffects = 0;
        when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
        when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);


@@ -1626,6 +1632,62 @@ public class BubblesTest extends SysuiTestCase {
                any(Bubble.class), anyBoolean(), anyBoolean());
                any(Bubble.class), anyBoolean(), anyBoolean());
    }
    }


    @Test
    public void testShowOrHideAppBubble_addsAndExpand() {
        assertThat(mBubbleController.isStackExpanded()).isFalse();
        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();

        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);

        verify(mBubbleController).inflateAndAdd(any(Bubble.class), /* suppressFlyout= */ eq(true),
                /* showInShade= */ eq(false));
        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
        assertThat(mBubbleController.isStackExpanded()).isTrue();
    }

    @Test
    public void testShowOrHideAppBubble_expandIfCollapsed() {
        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);
        mBubbleController.updateBubble(mBubbleEntry);
        mBubbleController.collapseStack();
        assertThat(mBubbleController.isStackExpanded()).isFalse();

        // Calling this while collapsed will expand the app bubble
        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);

        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
        assertThat(mBubbleController.isStackExpanded()).isTrue();
        assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
    }

    @Test
    public void testShowOrHideAppBubble_collapseIfSelected() {
        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);
        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
        assertThat(mBubbleController.isStackExpanded()).isTrue();

        // Calling this while the app bubble is expanded should collapse the stack
        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);

        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
        assertThat(mBubbleController.isStackExpanded()).isFalse();
        assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
    }

    @Test
    public void testShowOrHideAppBubble_selectIfNotSelected() {
        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);
        mBubbleController.updateBubble(mBubbleEntry);
        mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(mBubbleEntry.getKey());
        assertThat(mBubbleController.isStackExpanded()).isTrue();

        mBubbleController.showOrHideAppBubble(mAppBubbleIntent);
        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
        assertThat(mBubbleController.isStackExpanded()).isTrue();
        assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
    }

    /** Creates a bubble using the userId and package. */
    /** Creates a bubble using the userId and package. */
    private Bubble createBubble(int userId, String pkg) {
    private Bubble createBubble(int userId, String pkg) {
        final UserHandle userHandle = new UserHandle(userId);
        final UserHandle userHandle = new UserHandle(userId);