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

Commit 7593029a authored by Liran Binyamin's avatar Liran Binyamin Committed by Android (Google) Code Review
Browse files

Merge "Decouple BubbleController from some bubble classes" into main

parents 4ee4e6bc e388974d
Loading
Loading
Loading
Loading
+18 −14
Original line number Diff line number Diff line
@@ -173,7 +173,7 @@ public class BubbleController implements ConfigurationChangeListener,
    private final Context mContext;
    private final BubblesImpl mImpl = new BubblesImpl();
    private Bubbles.BubbleExpandListener mExpandListener;
    @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
    @Nullable private final BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
    private final FloatingContentCoordinator mFloatingContentCoordinator;
    private final BubbleDataRepository mDataRepository;
    private final WindowManagerShellWrapper mWindowManagerShellWrapper;
@@ -197,12 +197,12 @@ public class BubbleController implements ConfigurationChangeListener,
    private final Handler mMainHandler;
    private final ShellExecutor mBackgroundExecutor;

    private BubbleLogger mLogger;
    private BubbleData mBubbleData;
    private final BubbleLogger mLogger;
    private final BubbleData mBubbleData;
    @Nullable private BubbleStackView mStackView;
    @Nullable private BubbleBarLayerView mLayerView;
    private BubbleIconFactory mBubbleIconFactory;
    private BubblePositioner mBubblePositioner;
    private final BubblePositioner mBubblePositioner;
    private Bubbles.SysuiProxy mSysuiProxy;

    // Tracks the id of the current (foreground) user.
@@ -232,13 +232,17 @@ public class BubbleController implements ConfigurationChangeListener,
    /** Whether or not the BubbleStackView has been added to the WindowManager. */
    private boolean mAddedToWindowManager = false;

    /** Saved screen density, used to detect display size changes in {@link #onConfigChanged}. */
    /**
     * Saved screen density, used to detect display size changes in {@link #onConfigurationChanged}.
     */
    private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED;

    /** Saved screen bounds, used to detect screen size changes in {@link #onConfigChanged}. **/
    private Rect mScreenBounds = new Rect();
    /**
     * Saved screen bounds, used to detect screen size changes in {@link #onConfigurationChanged}.
     */
    private final Rect mScreenBounds = new Rect();

    /** Saved font scale, used to detect font size changes in {@link #onConfigChanged}. */
    /** Saved font scale, used to detect font size changes in {@link #onConfigurationChanged}. */
    private float mFontScale = 0;

    /** Saved direction, used to detect layout direction changes @link #onConfigChanged}. */
@@ -253,9 +257,9 @@ public class BubbleController implements ConfigurationChangeListener,
    private boolean mIsStatusBarShade = true;

    /** One handed mode controller to register transition listener. */
    private Optional<OneHandedController> mOneHandedOptional;
    private final Optional<OneHandedController> mOneHandedOptional;
    /** Drag and drop controller to register listener for onDragStarted. */
    private Optional<DragAndDropController> mDragAndDropController;
    private final Optional<DragAndDropController> mDragAndDropController;
    /** Used to send bubble events to launcher. */
    private Bubbles.BubbleStateListener mBubbleStateListener;

@@ -731,9 +735,11 @@ public class BubbleController implements ConfigurationChangeListener,
            }
        } else {
            if (mStackView == null) {
                BubbleStackViewManager bubbleStackViewManager =
                        BubbleStackViewManager.fromBubbleController(this);
                mStackView = new BubbleStackView(
                        mContext, this, mBubbleData, mSurfaceSynchronizer,
                        mFloatingContentCoordinator, this, mMainExecutor);
                        mContext, bubbleStackViewManager, mBubblePositioner, mBubbleData,
                        mSurfaceSynchronizer, mFloatingContentCoordinator, this, mMainExecutor);
                mStackView.onOrientationChanged();
                if (mExpandListener != null) {
                    mStackView.setExpandListener(mExpandListener);
@@ -893,7 +899,6 @@ public class BubbleController implements ConfigurationChangeListener,
     * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
     * added in the meantime.
     */
    @VisibleForTesting
    public void onAllBubblesAnimatedOut() {
        if (mStackView != null) {
            mStackView.setVisibility(INVISIBLE);
@@ -1047,7 +1052,6 @@ public class BubbleController implements ConfigurationChangeListener,
        return mBubbleData.hasBubbles() || mBubbleData.isShowingOverflow();
    }

    @VisibleForTesting
    public boolean isStackExpanded() {
        return mBubbleData.isExpanded();
    }
+16 −10
Original line number Diff line number Diff line
@@ -204,7 +204,7 @@ public class BubbleStackView extends FrameLayout
                    Choreographer.getInstance().postFrameCallback(frameCallback);
                }
            };
    private final BubbleController mBubbleController;
    private final BubbleStackViewManager mManager;
    private final BubbleData mBubbleData;
    private final Bubbles.SysuiProxy.Provider mSysuiProxyProvider;
    private StackViewState mStackViewState = new StackViewState();
@@ -858,6 +858,7 @@ public class BubbleStackView extends FrameLayout

    private BubbleOverflow mBubbleOverflow;
    private StackEducationView mStackEduView;
    private StackEducationView.Manager mStackEducationViewManager;
    private ManageEducationView mManageEduView;
    private DismissView mDismissView;

@@ -873,15 +874,16 @@ public class BubbleStackView extends FrameLayout
    private BubblePositioner mPositioner;

    @SuppressLint("ClickableViewAccessibility")
    public BubbleStackView(Context context, BubbleController bubbleController,
            BubbleData data, @Nullable SurfaceSynchronizer synchronizer,
    public BubbleStackView(Context context, BubbleStackViewManager bubbleStackViewManager,
            BubblePositioner bubblePositioner, BubbleData data,
            @Nullable SurfaceSynchronizer synchronizer,
            FloatingContentCoordinator floatingContentCoordinator,
            Bubbles.SysuiProxy.Provider sysuiProxyProvider,
            ShellExecutor mainExecutor) {
        super(context);

        mMainExecutor = mainExecutor;
        mBubbleController = bubbleController;
        mManager = bubbleStackViewManager;
        mBubbleData = data;
        mSysuiProxyProvider = sysuiProxyProvider;

@@ -893,7 +895,7 @@ public class BubbleStackView extends FrameLayout
        mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
        int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);

        mPositioner = mBubbleController.getPositioner();
        mPositioner = bubblePositioner;

        final TypedArray ta = mContext.obtainStyledAttributes(
                new int[]{android.R.attr.dialogCornerRadius});
@@ -903,7 +905,7 @@ public class BubbleStackView extends FrameLayout
        final Runnable onBubbleAnimatedOut = () -> {
            if (getBubbleCount() == 0) {
                mExpandedViewTemporarilyHidden = false;
                mBubbleController.onAllBubblesAnimatedOut();
                mManager.onAllBubblesAnimatedOut();
            }
        };
        mStackAnimationController = new StackAnimationController(
@@ -1383,7 +1385,9 @@ public class BubbleStackView extends FrameLayout
            return false;
        }
        if (mStackEduView == null) {
            mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
            mStackEducationViewManager = mManager::updateWindowFlagsForBackpress;
            mStackEduView =
                    new StackEducationView(mContext, mPositioner, mStackEducationViewManager);
            addView(mStackEduView);
        }
        return showStackEdu();
@@ -1412,7 +1416,9 @@ public class BubbleStackView extends FrameLayout
    private void updateUserEdu() {
        if (isStackEduVisible() && !mStackEduView.isHiding()) {
            removeView(mStackEduView);
            mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
            mStackEducationViewManager = mManager::updateWindowFlagsForBackpress;
            mStackEduView =
                    new StackEducationView(mContext, mPositioner, mStackEducationViewManager);
            addView(mStackEduView);
            showStackEdu();
        }
@@ -2106,7 +2112,7 @@ public class BubbleStackView extends FrameLayout
            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
            logBubbleEvent(mExpandedBubble,
                    FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
            mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
            mManager.checkNotificationPanelExpandedState(notifPanelExpanded -> {
                if (!notifPanelExpanded && mIsExpanded) {
                    startMonitoringSwipeUpGesture();
                }
@@ -2227,7 +2233,7 @@ public class BubbleStackView extends FrameLayout
     */
    void hideCurrentInputMethod() {
        mPositioner.setImeVisible(false, 0);
        mBubbleController.hideCurrentInputMethod();
        mManager.hideCurrentInputMethod();
    }

    /** Set the stack position to whatever the positioner says. */
+60 −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

import java.util.function.Consumer

/** Defines callbacks from [BubbleStackView] to its manager. */
interface BubbleStackViewManager {

    /** Notifies that all bubbles animated out. */
    fun onAllBubblesAnimatedOut()

    /** Notifies whether backpress should be intercepted. */
    fun updateWindowFlagsForBackpress(interceptBack: Boolean)

    /**
     * Checks the current expansion state of the notification panel, and invokes [callback] with the
     * result.
     */
    fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>)

    /** Requests to hide the current input method. */
    fun hideCurrentInputMethod()

    companion object {

        @JvmStatic
        fun fromBubbleController(controller: BubbleController) = object : BubbleStackViewManager {
            override fun onAllBubblesAnimatedOut() {
                controller.onAllBubblesAnimatedOut()
            }

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

            override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {
                controller.isNotificationPanelExpanded(callback)
            }

            override fun hideCurrentInputMethod() {
                controller.hideCurrentInputMethod()
            }
        }
    }
}
+10 −4
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ import com.android.wm.shell.animation.Interpolators
class StackEducationView(
    context: Context,
    private val positioner: BubblePositioner,
    private val controller: BubbleController
    private val manager: Manager
) : LinearLayout(context) {

    companion object {
@@ -44,6 +44,12 @@ class StackEducationView(
        private const val ANIMATE_DURATION_SHORT: Long = 40
    }

    /** Callbacks to notify managers of [StackEducationView] about events. */
    interface Manager {
        /** Notifies whether backpress should be intercepted. */
        fun updateWindowFlagsForBackpress(interceptBack: Boolean)
    }

    private val view by lazy { requireViewById<View>(R.id.stack_education_layout) }
    private val titleTextView by lazy { requireViewById<TextView>(R.id.stack_education_title) }
    private val descTextView by lazy { requireViewById<TextView>(R.id.stack_education_description) }
@@ -93,7 +99,7 @@ class StackEducationView(
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        setOnKeyListener(null)
        controller.updateWindowFlagsForBackpress(false /* interceptBack */)
        manager.updateWindowFlagsForBackpress(false /* interceptBack */)
    }

    private fun setTextColor() {
@@ -124,7 +130,7 @@ class StackEducationView(
        isHiding = false
        if (visibility == VISIBLE) return false

        controller.updateWindowFlagsForBackpress(true /* interceptBack */)
        manager.updateWindowFlagsForBackpress(true /* interceptBack */)
        layoutParams.width =
                if (positioner.isLargeScreen || positioner.isLandscape)
                    context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
@@ -185,7 +191,7 @@ class StackEducationView(
        if (visibility != VISIBLE || isHiding) return
        isHiding = true

        controller.updateWindowFlagsForBackpress(false /* interceptBack */)
        manager.updateWindowFlagsForBackpress(false /* interceptBack */)
        animate()
            .alpha(0f)
            .setDuration(if (isExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+73 −28
Original line number Diff line number Diff line
@@ -56,9 +56,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

/**
 * Tests for loading / inflating views & icons for a bubble.
 */
/** Tests for loading / inflating views & icons for a bubble. */
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@@ -76,25 +74,33 @@ class BubbleViewInfoTest : ShellTestCase() {
    @Before
    fun setup() {
        metadataFlagListener = Bubbles.BubbleMetadataFlagListener {}
        iconFactory = BubbleIconFactory(context,
        iconFactory =
            BubbleIconFactory(
                context,
                60,
                30,
                Color.RED,
                mContext.resources.getDimensionPixelSize(
                        R.dimen.importance_ring_stroke_width))
                mContext.resources.getDimensionPixelSize(R.dimen.importance_ring_stroke_width)
            )

        mainExecutor = TestShellExecutor()
        val windowManager = context.getSystemService(WindowManager::class.java)
        val shellInit = ShellInit(mainExecutor)
        val shellCommandHandler = ShellCommandHandler()
        val shellController = ShellController(context, shellInit, shellCommandHandler,
                mainExecutor)
        val shellController = ShellController(context, shellInit, shellCommandHandler, mainExecutor)
        val bubblePositioner = BubblePositioner(context, windowManager)
        val bubbleData = BubbleData(context, mock<BubbleLogger>(), bubblePositioner,
                BubbleEducationController(context), mainExecutor)
        val bubbleData =
            BubbleData(
                context,
                mock<BubbleLogger>(),
                bubblePositioner,
                BubbleEducationController(context),
                mainExecutor
            )
        val surfaceSynchronizer = { obj: Runnable -> obj.run() }

        bubbleController = BubbleController(
        bubbleController =
            BubbleController(
                context,
                shellInit,
                shellCommandHandler,
@@ -122,18 +128,36 @@ class BubbleViewInfoTest : ShellTestCase() {
                mock<Transitions>(),
                mock<SyncTransactionQueue>(),
                mock<IWindowManager>(),
                mock<BubbleProperties>())
                mock<BubbleProperties>()
            )

        bubbleStackView = BubbleStackView(context, bubbleController, bubbleData,
                surfaceSynchronizer, FloatingContentCoordinator(), bubbleController, mainExecutor)
        val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
        bubbleStackView =
            BubbleStackView(
                context,
                bubbleStackViewManager,
                bubblePositioner,
                bubbleData,
                surfaceSynchronizer,
                FloatingContentCoordinator(),
                bubbleController,
                mainExecutor
            )
        bubbleBarLayerView = BubbleBarLayerView(context, bubbleController)
    }

    @Test
    fun testPopulate() {
        bubble = createBubbleWithShortcut()
        val info = BubbleViewInfoTask.BubbleViewInfo.populate(context,
                bubbleController, bubbleStackView, iconFactory, bubble, false /* skipInflation */)
        val info =
            BubbleViewInfoTask.BubbleViewInfo.populate(
                context,
                bubbleController,
                bubbleStackView,
                iconFactory,
                bubble,
                false /* skipInflation */
            )
        assertThat(info!!).isNotNull()

        assertThat(info.imageView).isNotNull()
@@ -151,9 +175,15 @@ class BubbleViewInfoTest : ShellTestCase() {
    @Test
    fun testPopulateForBubbleBar() {
        bubble = createBubbleWithShortcut()
        val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
                bubbleController, bubbleBarLayerView, iconFactory, bubble,
                false /* skipInflation */)
        val info =
            BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                context,
                bubbleController,
                bubbleBarLayerView,
                iconFactory,
                bubble,
                false /* skipInflation */
            )
        assertThat(info!!).isNotNull()

        assertThat(info.imageView).isNull()
@@ -176,12 +206,18 @@ class BubbleViewInfoTest : ShellTestCase() {
        // exception here if the app has an issue loading the shortcut icon; we default to
        // the app icon in that case / none of the icons will be null.
        val mockIconFactory = mock<BubbleIconFactory>()
        whenever(mockIconFactory.getBubbleDrawable(eq(context), eq(bubble.shortcutInfo),
                any())).doThrow(RuntimeException())
        whenever(mockIconFactory.getBubbleDrawable(eq(context), eq(bubble.shortcutInfo), any()))
            .doThrow(RuntimeException())

        val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(context,
                bubbleController, bubbleBarLayerView, iconFactory, bubble,
                true /* skipInflation */)
        val info =
            BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                context,
                bubbleController,
                bubbleBarLayerView,
                iconFactory,
                bubble,
                true /* skipInflation */
            )
        assertThat(info).isNotNull()

        assertThat(info?.shortcutInfo).isNotNull()
@@ -194,8 +230,17 @@ class BubbleViewInfoTest : ShellTestCase() {

    private fun createBubbleWithShortcut(): Bubble {
        val shortcutInfo = ShortcutInfo.Builder(mContext, "mockShortcutId").build()
        return Bubble("mockKey", shortcutInfo, 1000, Resources.ID_NULL,
                "mockTitle", 0 /* taskId */, "mockLocus", true /* isDismissible */,
                mainExecutor, metadataFlagListener)
        return Bubble(
            "mockKey",
            shortcutInfo,
            1000,
            Resources.ID_NULL,
            "mockTitle",
            0 /* taskId */,
            "mockLocus",
            true /* isDismissible */,
            mainExecutor,
            metadataFlagListener
        )
    }
}