From 788a6ebb7499e48933d4ffca897d53e15f86bb94 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Thu, 15 Aug 2024 16:03:07 -0700 Subject: [PATCH] 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 --- .../taskbar/TaskbarActivityContext.java | 9 + .../bubbles/BubbleBarViewController.java | 9 + .../BubbleStashedHandleViewController.java | 14 +- .../com/android/quickstep/InputConsumer.java | 2 + .../quickstep/TouchInteractionService.java | 17 +- .../BubbleBarInputConsumer.java | 147 +++++++++++++++++ .../TaskbarUnstashInputConsumer.java | 82 +-------- .../bubbles/BubbleBarInputConsumerTest.kt | 156 ++++++++++++++++++ 8 files changed, 345 insertions(+), 91 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java create mode 100644 quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index cd1eea2f0a..1471234925 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -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 */ diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 4fe4ace5d1..f66b350ec9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -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(); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 8158fe79b4..6bfe8f40ae 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -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 */ diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index f898e2f002..0185737c1d 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -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; diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 88ab5283f2..2b5aa71125 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -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 diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java new file mode 100644 index 0000000000..dbe20685d5 --- /dev/null +++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java @@ -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; + } +} diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index 9a99d4a448..17a97fae21 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -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. * diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt new file mode 100644 index 0000000000..785ec66420 --- /dev/null +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt @@ -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() + } +}