Make an input consumer for bubble bar

Instead of unstashing bubble bar in TaskbarUnstashInputConsumer
handle it in its own input consumer instead. This is a nicer
separation of concerns and simplifies the logic in both places.

Additionally, this fixes issues where swiping up the bubble bar
handle might initiate / be handled by gesture nav (or all apps) when
really any events originating on bubble UI should be managed by
bubbles instead.

Adds some mostly mock'd tests to check that the static method used
to indicate if bubbles should take the event or not works correctly.

Flag: com.android.wm.shell.enable_bubble_bar
Test: manual - test swipe up on bubble bar handle in an app
             - test swipe up on bubble bar on home - it should
               open & all apps / launcher transition shouldn't happen
             - longpress to drag bubbles around on home and overview,
               try the collapsed bar & individual bubbles in the
               expanded bar
             - test dragging on the bubble bar after it's revealed
               in an app
Test: atest BubbleBarInputConsumerTest
Bug: 345488529
Change-Id: I964d213d71de15e4350cff5202dfb9343de1af14
This commit is contained in:
Mady Mellor
2024-08-15 16:03:07 -07:00
parent d22a0b84cb
commit 788a6ebb74
8 changed files with 345 additions and 91 deletions
@@ -501,6 +501,15 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
return enableTinyTaskbar() && mDeviceProfile.isPhone && mDeviceProfile.isTaskbarPresent;
}
/**
* Returns {@code true} iff bubble bar is enabled (but not necessarily visible /
* containing bubbles).
*/
@Override
public boolean isBubbleBarEnabled() {
return getBubbleControllers() != null && BubbleBarController.isBubbleBarEnabled();
}
/**
* Returns if software keyboard is docked or input toolbar is placed at the taskbar area
*/
@@ -308,6 +308,15 @@ public class BubbleBarViewController {
return mBarView.getBubbleBarBounds();
}
/** Checks that bubble bar is visible and that the motion event is within bounds. */
public boolean isEventOverBubbleBar(MotionEvent event) {
if (!isBubbleBarVisible()) return false;
final Rect bounds = getBubbleBarBounds();
final int bubbleBarTopOnScreen = mBarView.getRestingTopPositionOnScreen();
final float x = event.getX();
return event.getRawY() >= bubbleBarTopOnScreen && x >= bounds.left && x <= bounds.right;
}
/** Whether a new bubble is animating. */
public boolean isAnimatingNewBubble() {
return mBubbleBarViewAnimator != null && mBubbleBarViewAnimator.isAnimating();
@@ -298,15 +298,11 @@ public class BubbleStashedHandleViewController {
}
// the bounds of the handle only include the visible part, so we check that the Y coordinate
// is anywhere within the stashed taskbar height.
int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
return (int) ev.getRawY() >= top && containsX((int) ev.getRawX());
}
/** Checks if the given x coordinate is within the stashed handle bounds. */
public boolean containsX(int x) {
return x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right;
// is anywhere within the stashed height of bubble bar (same as taskbar stashed height).
final int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
final float x = ev.getRawX();
return ev.getRawY() >= top && x >= mStashedHandleBounds.left
&& x <= mStashedHandleBounds.right;
}
/** Set a bubble bar location */
@@ -40,6 +40,7 @@ public interface InputConsumer {
int TYPE_STATUS_BAR = 1 << 13;
int TYPE_CURSOR_HOVER = 1 << 14;
int TYPE_NAV_HANDLE_LONG_PRESS = 1 << 15;
int TYPE_BUBBLE_BAR = 1 << 16;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -58,6 +59,7 @@ public interface InputConsumer {
"TYPE_STATUS_BAR", // 13
"TYPE_CURSOR_HOVER", // 14
"TYPE_NAV_HANDLE_LONG_PRESS", // 15
"TYPE_BUBBLE_BAR", // 16
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -97,6 +97,7 @@ import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -109,6 +110,7 @@ import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -901,11 +903,14 @@ public class TouchInteractionService extends Service {
boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive();
boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event);
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
boolean isOnBubbles = bubbleControllers != null
&& BubbleBarInputConsumer.isEventOnBubbles(tac, event);
if (isInSwipeUpTouchRegion && tac != null) {
tac.closeKeyboardQuickSwitchView();
}
if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
|| isHoverActionWithoutConsumer) {
|| isHoverActionWithoutConsumer || isOnBubbles) {
reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
? "one handed mode is not active and event is in swipe up region"
: "isHoverActionWithoutConsumer == true")
@@ -1085,6 +1090,15 @@ public class TouchInteractionService extends Service {
private InputConsumer newConsumer(
GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
InputConsumer consumer = new BubbleBarInputConsumer(this, bubbleControllers,
mInputMonitorCompat);
logInputConsumerSelectionReason(consumer, newCompoundString(
"event is on bubbles, creating new input consumer"));
return consumer;
}
AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
if (progressProxy != null) {
InputConsumer consumer = new ProgressDelegateInputConsumer(
@@ -1149,7 +1163,6 @@ public class TouchInteractionService extends Service {
}
// If Taskbar is present, we listen for swipe or cursor hover events to unstash it.
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
if (tac != null && !(base instanceof AssistantInputConsumer)) {
// Present always on large screen or on small screen w/ flag
boolean useTaskbarConsumer = tac.getDeviceProfile().isTaskbarPresent
@@ -0,0 +1,147 @@
/*
* 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.quickstep.inputconsumers;
import static android.view.MotionEvent.INVALID_POINTER_ID;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.bubbles.BubbleDragController;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
* Listens for touch events on the bubble bar.
*/
public class BubbleBarInputConsumer implements InputConsumer {
private final BubbleStashController mBubbleStashController;
private final BubbleBarViewController mBubbleBarViewController;
private final BubbleDragController mBubbleDragController;
private final InputMonitorCompat mInputMonitorCompat;
private boolean mSwipeUpOnBubbleHandle;
private boolean mPassedTouchSlop;
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private final long mTimeForTap;
private int mActivePointerId = INVALID_POINTER_ID;
public BubbleBarInputConsumer(Context context, BubbleControllers bubbleControllers,
InputMonitorCompat inputMonitorCompat) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDragController = bubbleControllers.bubbleDragController;
mInputMonitorCompat = inputMonitorCompat;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTimeForTap = ViewConfiguration.getTapTimeout();
}
@Override
public int getType() {
return TYPE_BUBBLE_BAR;
}
@Override
public void onMotionEvent(MotionEvent ev) {
final boolean isStashed = mBubbleStashController.isStashed();
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
break;
case MotionEvent.ACTION_MOVE:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == INVALID_POINTER_ID) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
float dX = mLastPos.x - mDownPos.x;
float dY = mLastPos.y - mDownPos.y;
if (!mPassedTouchSlop) {
mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
}
if ((isCollapsed() || isStashed) && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) {
boolean verticalGesture = Math.abs(dY) > Math.abs(dX);
if (verticalGesture && !mBubbleDragController.isDragging()) {
mSwipeUpOnBubbleHandle = true;
mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
// Bubbles is handling the swipe so make sure no one else gets it.
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
}
}
break;
case MotionEvent.ACTION_UP:
boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap;
if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop) {
// Taps on the handle / collapsed state should open the bar
if (isStashed || isCollapsed()) {
mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
}
}
break;
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
cleanupAfterMotionEvent();
}
}
private void cleanupAfterMotionEvent() {
mPassedTouchSlop = false;
mSwipeUpOnBubbleHandle = false;
}
private boolean isCollapsed() {
return mBubbleStashController.isBubbleBarVisible()
&& !mBubbleBarViewController.isExpanded();
}
/**
* Returns whether the event is occurring on a visible bubble bar or the bar handle.
*/
public static boolean isEventOnBubbles(TaskbarActivityContext tac, MotionEvent ev) {
if (tac == null || !tac.isBubbleBarEnabled()) {
return false;
}
BubbleControllers controllers = tac.getBubbleControllers();
if (controllers == null || !controllers.bubbleBarViewController.hasBubbles()) {
return false;
}
if (controllers.bubbleStashController.isStashed()
&& controllers.bubbleStashedHandleViewController.isPresent()) {
return controllers.bubbleStashedHandleViewController.get().isEventOverHandle(ev);
} else if (controllers.bubbleBarViewController.isBubbleBarVisible()) {
return controllers.bubbleBarViewController.isEventOverBubbleBar(ev);
}
return false;
}
}
@@ -15,9 +15,7 @@
*/
package com.android.quickstep.inputconsumers;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.launcher3.Flags.enableCursorHoverStates;
@@ -43,7 +41,6 @@ import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState;
@@ -69,9 +66,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
private final int mTaskbarNavThresholdY;
private final boolean mIsTaskbarAllAppsOpen;
private boolean mHasPassedTaskbarNavThreshold;
private boolean mIsInBubbleBarArea;
private boolean mIsVerticalGestureOverBubbleBar;
private boolean mIsPassedBubbleBarSlop;
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
@@ -159,9 +153,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
mTransitionCallback.onActionDown();
}
if (mIsTransientTaskbar && isInBubbleBarArea(x)) {
mIsInBubbleBarArea = true;
}
break;
case MotionEvent.ACTION_POINTER_UP:
int ptrIdx = ev.getActionIndex();
@@ -185,18 +176,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
float dX = mLastPos.x - mDownPos.x;
float dY = mLastPos.y - mDownPos.y;
if (!mIsPassedBubbleBarSlop && mIsInBubbleBarArea) {
boolean passedSlop =
Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
if (passedSlop) {
mIsPassedBubbleBarSlop = true;
mIsVerticalGestureOverBubbleBar = Math.abs(dY) > Math.abs(dX);
if (mIsVerticalGestureOverBubbleBar) {
setActive(ev);
}
}
}
if (mIsTransientTaskbar) {
boolean passedTaskbarNavThreshold = dY < 0
&& Math.abs(dY) >= mTaskbarNavThreshold;
@@ -204,11 +183,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold
&& !mGestureState.isInExtendedSlopRegion()) {
mHasPassedTaskbarNavThreshold = true;
if (mIsInBubbleBarArea && mIsVerticalGestureOverBubbleBar) {
mTaskbarActivityContext.onSwipeToOpenBubblebar();
} else {
mTaskbarActivityContext.onSwipeToUnstashTaskbar();
}
mTaskbarActivityContext.onSwipeToUnstashTaskbar();
}
if (dY < 0) {
@@ -230,41 +205,8 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
break;
}
}
boolean isMovingInBubbleBarArea = mIsInBubbleBarArea && ev.getAction() == ACTION_MOVE;
if (!isStashedTaskbarHovered) {
// if we're moving in the bubble bar area but we haven't passed the slop yet, don't
// propagate to the delegate, until we can determine the direction of the gesture.
if (!isMovingInBubbleBarArea || mIsPassedBubbleBarSlop) {
mDelegate.onMotionEvent(ev);
}
}
} else if (mIsVerticalGestureOverBubbleBar) {
// if we get here then this gesture is a vertical swipe over the bubble bar.
// we're also active and there's no need to delegate any additional motion events. the
// rest of the gesture will be handled here.
switch (ev.getAction()) {
case ACTION_MOVE:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == INVALID_POINTER_ID) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
float dY = mLastPos.y - mDownPos.y;
// bubble bar swipe gesture uses the same threshold as the taskbar.
boolean passedTaskbarNavThreshold = dY < 0
&& Math.abs(dY) >= mTaskbarNavThreshold;
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
mHasPassedTaskbarNavThreshold = true;
mTaskbarActivityContext.onSwipeToOpenBubblebar();
}
break;
case ACTION_UP:
case ACTION_CANCEL:
cleanupAfterMotionEvent();
break;
mDelegate.onMotionEvent(ev);
}
}
}
@@ -301,9 +243,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
mTransitionCallback.onActionEnd();
}
mHasPassedTaskbarNavThreshold = false;
mIsInBubbleBarArea = false;
mIsVerticalGestureOverBubbleBar = false;
mIsPassedBubbleBarSlop = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -313,23 +252,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer {
mMotionMoveCount = 0;
}
private boolean isInBubbleBarArea(float x) {
if (mTaskbarActivityContext == null || !mIsTransientTaskbar) {
return false;
}
BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers();
if (controllers == null) {
return false;
}
if (controllers.bubbleStashController.isStashed()
&& controllers.bubbleStashedHandleViewController.isPresent()) {
return controllers.bubbleStashedHandleViewController.get().containsX((int) x);
} else {
Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds();
return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right;
}
}
/**
* Listen for hover events for the stashed taskbar.
*
@@ -0,0 +1,156 @@
/*
* 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.launcher3.taskbar.bubbles
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
/**
* Tests for bubble bar input consumer, namely the static method that indicates whether the input
* consumer should handle the event.
*/
@RunWith(AndroidJUnit4::class)
class BubbleBarInputConsumerTest {
private lateinit var bubbleControllers: BubbleControllers
@Mock private lateinit var taskbarActivityContext: TaskbarActivityContext
@Mock private lateinit var bubbleBarController: BubbleBarController
@Mock private lateinit var bubbleBarViewController: BubbleBarViewController
@Mock private lateinit var bubbleStashController: BubbleStashController
@Mock private lateinit var bubbleStashedHandleViewController: BubbleStashedHandleViewController
@Mock private lateinit var bubbleDragController: BubbleDragController
@Mock private lateinit var bubbleDismissController: BubbleDismissController
@Mock private lateinit var bubbleBarPinController: BubbleBarPinController
@Mock private lateinit var bubblePinController: BubblePinController
@Mock private lateinit var bubbleCreator: BubbleCreator
@Mock private lateinit var motionEvent: MotionEvent
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
bubbleControllers =
BubbleControllers(
bubbleBarController,
bubbleBarViewController,
bubbleStashController,
Optional.of(bubbleStashedHandleViewController),
bubbleDragController,
bubbleDismissController,
bubbleBarPinController,
bubblePinController,
bubbleCreator
)
}
@Test
fun testIsEventOnBubbles_noTaskbarActivityContext() {
assertThat(BubbleBarInputConsumer.isEventOnBubbles(null, motionEvent)).isFalse()
}
@Test
fun testIsEventOnBubbles_bubblesNotEnabled() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(false)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isFalse()
}
@Test
fun testIsEventOnBubbles_noBubbleControllers() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(null)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isFalse()
}
@Test
fun testIsEventOnBubbles_noBubbles() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isFalse()
}
@Test
fun testIsEventOnBubbles_eventOnStashedHandle() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleStashController.isStashed).thenReturn(true)
whenever(bubbleStashedHandleViewController.isEventOverHandle(any())).thenReturn(true)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isTrue()
}
@Test
fun testIsEventOnBubbles_eventNotOnStashedHandle() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleStashController.isStashed).thenReturn(true)
whenever(bubbleStashedHandleViewController.isEventOverHandle(any())).thenReturn(false)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isFalse()
}
@Test
fun testIsEventOnBubbles_eventOnVisibleBubbleView() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleStashController.isStashed).thenReturn(false)
whenever(bubbleBarViewController.isBubbleBarVisible).thenReturn(true)
whenever(bubbleBarViewController.isEventOverBubbleBar(any())).thenReturn(true)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isTrue()
}
@Test
fun testIsEventOnBubbles_eventNotOnVisibleBubbleView() {
whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
whenever(bubbleStashController.isStashed).thenReturn(false)
whenever(bubbleBarViewController.isBubbleBarVisible).thenReturn(true)
whenever(bubbleBarViewController.isEventOverBubbleBar(any())).thenReturn(false)
assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
.isFalse()
}
}