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

Commit e258aad9 authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Decouple BubbleController from more classes

This change creates BubbleExpandedViewManager and BubbleTaskViewFactory interfaces.

These interfaces allow decoupling BubbleController from more classes so it's easier to write unit tests for Bubble classes.

After this change it's possible to create a unit test for BubbleStackView that does not depend on BubbleController, but there is a blocking robolectric issue b/323188766, so I will add the test once that issue is resolved.

Flag: NA
Bug: 321265700
Test: atest BubblesTest
Test: atest BubbleViewInfoTest
Test: atest BubbleOverflowTest
Test: manual -- build and play with bubbles
Change-Id: Id1c79643c2ad9e88173d6c25a9e88643afaf5564
parent 7ac3ea3f
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -53,8 +53,6 @@ import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTaskController;

import java.io.PrintWriter;
import java.util.List;
@@ -403,13 +401,9 @@ public class Bubble implements BubbleViewProvider {
     * Returns the existing {@link #mBubbleTaskView} if it's not {@code null}. Otherwise a new
     * instance of {@link BubbleTaskView} is created.
     */
    public BubbleTaskView getOrCreateBubbleTaskView(Context context, BubbleController controller) {
    public BubbleTaskView getOrCreateBubbleTaskView(BubbleTaskViewFactory taskViewFactory) {
        if (mBubbleTaskView == null) {
            TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context,
                    controller.getTaskOrganizer(),
                    controller.getTaskViewTransitions(), controller.getSyncTransactionQueue());
            TaskView taskView = new TaskView(context, taskViewTaskController);
            mBubbleTaskView = new BubbleTaskView(taskView, controller.getMainExecutor());
            mBubbleTaskView = taskViewFactory.create();
        }
        return mBubbleTaskView;
    }
@@ -514,14 +508,18 @@ public class Bubble implements BubbleViewProvider {
     *
     * @param callback the callback to notify one the bubble is ready to be displayed.
     * @param context the context for the bubble.
     * @param controller the bubble controller.
     * @param expandedViewManager the bubble expanded view manager.
     * @param taskViewFactory the task view factory used to create the task view for the bubble.
     * @param positioner the bubble positioner.
     * @param stackView the view the bubble is added to, iff showing as floating.
     * @param layerView the layer the bubble is added to, iff showing in the bubble bar.
     * @param iconFactory the icon factory use to create images for the bubble.
     * @param iconFactory the icon factory used to create images for the bubble.
     */
    void inflate(BubbleViewInfoTask.Callback callback,
            Context context,
            BubbleController controller,
            BubbleExpandedViewManager expandedViewManager,
            BubbleTaskViewFactory taskViewFactory,
            BubblePositioner positioner,
            @Nullable BubbleStackView stackView,
            @Nullable BubbleBarLayerView layerView,
            BubbleIconFactory iconFactory,
@@ -531,7 +529,9 @@ public class Bubble implements BubbleViewProvider {
        }
        mInflationTask = new BubbleViewInfoTask(this,
                context,
                controller,
                expandedViewManager,
                taskViewFactory,
                positioner,
                stackView,
                layerView,
                iconFactory,
+40 −7
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTaskController;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.Transitions;

@@ -190,6 +191,8 @@ public class BubbleController implements ConfigurationChangeListener,
    private final ShellCommandHandler mShellCommandHandler;
    private final IWindowManager mWmService;
    private final BubbleProperties mBubbleProperties;
    private final BubbleTaskViewFactory mBubbleTaskViewFactory;
    private final BubbleExpandedViewManager mExpandedViewManager;

    // Used to post to main UI thread
    private final ShellExecutor mMainExecutor;
@@ -333,6 +336,16 @@ public class BubbleController implements ConfigurationChangeListener,
        mWmService = wmService;
        mBubbleProperties = bubbleProperties;
        shellInit.addInitCallback(this::onInit, this);
        mBubbleTaskViewFactory = new BubbleTaskViewFactory() {
            @Override
            public BubbleTaskView create() {
                TaskViewTaskController taskViewTaskController = new TaskViewTaskController(
                        context, organizer, taskViewTransitions, syncQueue);
                TaskView taskView = new TaskView(context, taskViewTaskController);
                return new BubbleTaskView(taskView, mainExecutor);
            }
        };
        mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this);
    }

    private void registerOneHandedState(OneHandedController oneHanded) {
@@ -802,7 +815,13 @@ public class BubbleController implements ConfigurationChangeListener,
        try {
            mAddedToWindowManager = true;
            registerBroadcastReceiver();
            mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar());
            if (isShowingAsBubbleBar()) {
                mBubbleData.getOverflow().initializeForBubbleBar(
                        mExpandedViewManager, mBubblePositioner);
            } else {
                mBubbleData.getOverflow().initialize(
                        mExpandedViewManager, mStackView, mBubblePositioner);
            }
            // (TODO: b/273314541) some duplication in the inset listener
            if (isShowingAsBubbleBar()) {
                mWindowManager.addView(mLayerView, mWmLayoutParams);
@@ -981,7 +1000,9 @@ public class BubbleController implements ConfigurationChangeListener,
        for (Bubble b : mBubbleData.getBubbles()) {
            b.inflate(null /* callback */,
                    mContext,
                    this,
                    mExpandedViewManager,
                    mBubbleTaskViewFactory,
                    mBubblePositioner,
                    mStackView,
                    mLayerView,
                    mBubbleIconFactory,
@@ -990,7 +1011,9 @@ public class BubbleController implements ConfigurationChangeListener,
        for (Bubble b : mBubbleData.getOverflowBubbles()) {
            b.inflate(null /* callback */,
                    mContext,
                    this,
                    mExpandedViewManager,
                    mBubbleTaskViewFactory,
                    mBubblePositioner,
                    mStackView,
                    mLayerView,
                    mBubbleIconFactory,
@@ -1374,7 +1397,9 @@ public class BubbleController implements ConfigurationChangeListener,
                bubble.inflate(
                        (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
                        mContext,
                        this,
                        mExpandedViewManager,
                        mBubbleTaskViewFactory,
                        mBubblePositioner,
                        mStackView,
                        mLayerView,
                        mBubbleIconFactory,
@@ -1421,7 +1446,9 @@ public class BubbleController implements ConfigurationChangeListener,
            Bubble bubble = mBubbleData.getBubbles().get(i);
            bubble.inflate(callback,
                    mContext,
                    this,
                    mExpandedViewManager,
                    mBubbleTaskViewFactory,
                    mBubblePositioner,
                    mStackView,
                    mLayerView,
                    mBubbleIconFactory,
@@ -1496,8 +1523,14 @@ public class BubbleController implements ConfigurationChangeListener,
        // Lazy init stack view when a bubble is created
        ensureBubbleViewsAndWindowCreated();
        bubble.setInflateSynchronously(mInflateSynchronously);
        bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
                mContext, this, mStackView,  mLayerView,
        bubble.inflate(
                b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
                mContext,
                mExpandedViewManager,
                mBubbleTaskViewFactory,
                mBubblePositioner,
                mStackView,
                mLayerView,
                mBubbleIconFactory,
                false /* skipInflation */);
    }
+5 −5
Original line number Diff line number Diff line
@@ -37,10 +37,8 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.RemovedBubble;

@@ -180,7 +178,7 @@ public class BubbleData {
     * This interface reports changes to the state and appearance of bubbles which should be applied
     * as necessary to the UI.
     */
    interface Listener {
    public interface Listener {
        /** Reports changes have have occurred as a result of the most recent operation. */
        void applyUpdate(Update update);
    }
@@ -419,8 +417,10 @@ public class BubbleData {

    /**
     * When this method is called it is expected that all info in the bubble has completed loading.
     * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleController, BubbleStackView,
     * BubbleBarLayerView, BubbleIconFactory, boolean)
     * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
     * BubbleTaskViewFactory, BubblePositioner, BubbleStackView,
     * com.android.wm.shell.bubbles.bar.BubbleBarLayerView,
     * com.android.launcher3.icons.BubbleIconFactory, boolean)
     */
    void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
        mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
+11 −8
Original line number Diff line number Diff line
@@ -184,7 +184,7 @@ public class BubbleExpandedView extends LinearLayout {
    private boolean mIsOverflow;
    private boolean mIsClipping;

    private BubbleController mController;
    private BubbleExpandedViewManager mManager;
    private BubbleStackView mStackView;
    private BubblePositioner mPositioner;

@@ -261,7 +261,7 @@ public class BubbleExpandedView extends LinearLayout {
                    // the bubble again so we'll just remove it.
                    Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
                            + ", " + e.getMessage() + "; removing bubble");
                    mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
                    mManager.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
                }
            });
            mInitialized = true;
@@ -281,7 +281,7 @@ public class BubbleExpandedView extends LinearLayout {

            if (mBubble != null && mBubble.isAppBubble()) {
                // Let the controller know sooner what the taskId is.
                mController.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
                mManager.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
            }

            // With the task org, the taskAppeared callback will only happen once the task has
@@ -301,7 +301,7 @@ public class BubbleExpandedView extends LinearLayout {
            ProtoLog.d(WM_SHELL_BUBBLES, "onTaskRemovalStarted: taskId=%d bubble=%s",
                    taskId, getBubbleKey());
            if (mBubble != null) {
                mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
                mManager.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
            }
            if (mTaskView != null) {
                // Release the surface
@@ -421,17 +421,20 @@ public class BubbleExpandedView extends LinearLayout {
     * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
     * to be called after view inflate.
     */
    void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow,
    void initialize(BubbleExpandedViewManager expandedViewManager,
            BubbleStackView stackView,
            BubblePositioner positioner,
            boolean isOverflow,
            @Nullable BubbleTaskView bubbleTaskView) {
        mController = controller;
        mManager = expandedViewManager;
        mStackView = stackView;
        mIsOverflow = isOverflow;
        mPositioner = mController.getPositioner();
        mPositioner = positioner;

        if (mIsOverflow) {
            mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
                    R.layout.bubble_overflow_container, null /* root */);
            mOverflowView.setBubbleController(mController);
            mOverflowView.initialize(expandedViewManager, positioner);
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
            mExpandedViewContainer.addView(mOverflowView, lp);
            mExpandedViewContainer.setLayoutParams(
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.bubbles

/** Manager interface for bubble expanded views. */
interface BubbleExpandedViewManager {

    val overflowBubbles: List<Bubble>
    fun setOverflowListener(listener: BubbleData.Listener)
    fun collapseStack()
    fun updateWindowFlagsForBackpress(intercept: Boolean)
    fun promoteBubbleFromOverflow(bubble: Bubble)
    fun removeBubble(key: String, reason: Int)
    fun dismissBubble(bubble: Bubble, reason: Int)
    fun setAppBubbleTaskId(key: String, taskId: Int)
    fun isStackExpanded(): Boolean
    fun isShowingAsBubbleBar(): Boolean

    companion object {
        /**
         * Convenience function for creating a [BubbleExpandedViewManager] that delegates to the
         * given `controller`.
         */
        @JvmStatic
        fun fromBubbleController(controller: BubbleController): BubbleExpandedViewManager {
            return object : BubbleExpandedViewManager {

                override val overflowBubbles: List<Bubble>
                    get() = controller.overflowBubbles

                override fun setOverflowListener(listener: BubbleData.Listener) {
                    controller.setOverflowListener(listener)
                }

                override fun collapseStack() {
                    controller.collapseStack()
                }

                override fun updateWindowFlagsForBackpress(intercept: Boolean) {
                    controller.updateWindowFlagsForBackpress(intercept)
                }

                override fun promoteBubbleFromOverflow(bubble: Bubble) {
                    controller.promoteBubbleFromOverflow(bubble)
                }

                override fun removeBubble(key: String, reason: Int) {
                    controller.removeBubble(key, reason)
                }

                override fun dismissBubble(bubble: Bubble, reason: Int) {
                    controller.dismissBubble(bubble, reason)
                }

                override fun setAppBubbleTaskId(key: String, taskId: Int) {
                    controller.setAppBubbleTaskId(key, taskId)
                }

                override fun isStackExpanded(): Boolean = controller.isStackExpanded

                override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
            }
        }
    }
}
Loading