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

Commit 689af7a5 authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge "Extract logic related to dragging bubble bar views to other side" into main

parents 58146487 db1ba195
Loading
Loading
Loading
Loading
+263 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles.bar

import android.content.Context
import android.graphics.Insets
import android.graphics.PointF
import android.graphics.Rect
import android.view.View
import android.view.WindowManager
@@ -30,30 +31,43 @@ import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.bar.BubbleBarDropTargetController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.bubbles.bar.BubbleBarDropTargetController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.bubbles.bar.BubbleBarDropTargetController.Companion.DROP_TARGET_SCALE
import com.android.wm.shell.bubbles.bar.BubbleExpandedViewPinController.Companion.DROP_TARGET_SCALE
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith

/** Tests for [BubbleBarDropTargetController] */
/** Tests for [BubbleExpandedViewPinController] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleBarDropTargetControllerTest {
class BubbleExpandedViewPinControllerTest {

    companion object {
        @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()

        const val SCREEN_WIDTH = 2000
        const val SCREEN_HEIGHT = 1000

        const val BUBBLE_BAR_WIDTH = 100
        const val BUBBLE_BAR_HEIGHT = 50
    }

    private val context = ApplicationProvider.getApplicationContext<Context>()
    private lateinit var controller: BubbleBarDropTargetController
    private lateinit var positioner: BubblePositioner
    private lateinit var container: FrameLayout

    private lateinit var controller: BubbleExpandedViewPinController
    private lateinit var testListener: TestLocationChangeListener

    private val pointOnLeft = PointF(100f, 100f)
    private val pointOnRight = PointF(1900f, 500f)

    @Before
    fun setUp() {
        ProtoLog.REQUIRE_PROTOLOGTOOL = false
@@ -63,7 +77,7 @@ class BubbleBarDropTargetControllerTest {
        positioner.setShowingInBubbleBar(true)
        val deviceConfig =
            DeviceConfig(
                windowBounds = Rect(0, 0, 2000, 2600),
                windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                isLargeScreen = true,
                isSmallTablet = false,
                isLandscape = true,
@@ -71,87 +85,149 @@ class BubbleBarDropTargetControllerTest {
                insets = Insets.of(10, 20, 30, 40)
            )
        positioner.update(deviceConfig)
        positioner.bubbleBarBounds = Rect(1800, 2400, 1970, 2560)
        positioner.bubbleBarBounds =
            Rect(
                SCREEN_WIDTH - deviceConfig.insets.right - BUBBLE_BAR_WIDTH,
                SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT,
                SCREEN_WIDTH - deviceConfig.insets.right,
                SCREEN_HEIGHT - deviceConfig.insets.bottom
            )

        controller = BubbleBarDropTargetController(context, container, positioner)
        controller = BubbleExpandedViewPinController(context, container, positioner)
        testListener = TestLocationChangeListener()
        controller.setListener(testListener)
    }

    @After
    fun tearDown() {
        runOnMainSync { controller.onDragEnd() }
        waitForAnimateOut()
    }

    @Test
    fun show_moveLeftToRight_isVisibleWithExpectedBounds() {
        val expectedBoundsOnLeft = getExpectedDropTargetBounds(onLeft = true)
        val expectedBoundsOnRight = getExpectedDropTargetBounds(onLeft = false)
    fun onDragUpdate_stayOnSameSide() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
        }
        waitForAnimateIn()
        assertThat(dropTargetView).isNull()
        assertThat(testListener.locationChanges).isEmpty()
    }

    @Test
    fun onDragUpdate_toLeft() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
        }
        waitForAnimateIn()

        assertThat(dropTargetView).isNotNull()
        assertThat(dropTargetView!!.alpha).isEqualTo(1f)

        val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = true)
        assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
        assertThat(dropTargetView!!.layoutParams.height)
            .isEqualTo(expectedDropTargetBounds.height())

        assertThat(testListener.locationChanges).containsExactly(BubbleBarLocation.LEFT)
    }

        runOnMainSync { controller.show(BubbleBarLocation.LEFT) }
    @Test
    fun onDragUpdate_toLeftAndBackToRight() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
        }
        waitForAnimateIn()
        val viewOnLeft = getDropTargetView()
        assertThat(viewOnLeft).isNotNull()
        assertThat(viewOnLeft!!.alpha).isEqualTo(1f)
        assertThat(viewOnLeft.layoutParams.width).isEqualTo(expectedBoundsOnLeft.width())
        assertThat(viewOnLeft.layoutParams.height).isEqualTo(expectedBoundsOnLeft.height())
        assertThat(viewOnLeft.x).isEqualTo(expectedBoundsOnLeft.left)
        assertThat(viewOnLeft.y).isEqualTo(expectedBoundsOnLeft.top)

        runOnMainSync { controller.show(BubbleBarLocation.RIGHT) }
        assertThat(dropTargetView).isNotNull()

        runOnMainSync { controller.onDragUpdate(pointOnRight.x, pointOnRight.y) }
        // We have to wait for existing drop target to animate out and new to animate in
        waitForAnimateOut()
        waitForAnimateIn()
        val viewOnRight = getDropTargetView()
        assertThat(viewOnRight).isNotNull()
        assertThat(viewOnRight!!.alpha).isEqualTo(1f)
        assertThat(viewOnRight.layoutParams.width).isEqualTo(expectedBoundsOnRight.width())
        assertThat(viewOnRight.layoutParams.height).isEqualTo(expectedBoundsOnRight.height())
        assertThat(viewOnRight.x).isEqualTo(expectedBoundsOnRight.left)
        assertThat(viewOnRight.y).isEqualTo(expectedBoundsOnRight.top)

        assertThat(dropTargetView).isNotNull()
        assertThat(dropTargetView!!.alpha).isEqualTo(1f)

        val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = false)
        assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
        assertThat(dropTargetView!!.layoutParams.height)
            .isEqualTo(expectedDropTargetBounds.height())

        assertThat(testListener.locationChanges)
            .containsExactly(BubbleBarLocation.LEFT, BubbleBarLocation.RIGHT)
    }

    @Test
    fun toggleSetHidden_dropTargetShown_updatesAlpha() {
        runOnMainSync { controller.show(BubbleBarLocation.RIGHT) }
    fun onDragUpdate_toLeftInExclusionRect() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            // Exclusion rect is around the bottom center area of the screen
            controller.onDragUpdate(SCREEN_WIDTH / 2f - 50, SCREEN_HEIGHT - 100f)
        }
        waitForAnimateIn()
        val view = getDropTargetView()
        assertThat(view).isNotNull()
        assertThat(view!!.alpha).isEqualTo(1f)
        assertThat(dropTargetView).isNull()
        assertThat(testListener.locationChanges).isEmpty()
    }

        runOnMainSync { controller.setHidden(true) }
    @Test
    fun toggleSetDropTargetHidden_dropTargetExists() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
        }
        waitForAnimateIn()

        assertThat(dropTargetView).isNotNull()
        assertThat(dropTargetView!!.alpha).isEqualTo(1f)

        runOnMainSync { controller.setDropTargetHidden(true) }
        waitForAnimateOut()
        val hiddenView = getDropTargetView()
        assertThat(hiddenView).isNotNull()
        assertThat(hiddenView!!.alpha).isEqualTo(0f)
        assertThat(dropTargetView).isNotNull()
        assertThat(dropTargetView!!.alpha).isEqualTo(0f)

        runOnMainSync { controller.setHidden(false) }
        runOnMainSync { controller.setDropTargetHidden(false) }
        waitForAnimateIn()
        val shownView = getDropTargetView()
        assertThat(shownView).isNotNull()
        assertThat(shownView!!.alpha).isEqualTo(1f)
        assertThat(dropTargetView).isNotNull()
        assertThat(dropTargetView!!.alpha).isEqualTo(1f)
    }

    @Test
    fun toggleSetHidden_dropTargetNotShown_viewNotCreated() {
        runOnMainSync { controller.setHidden(true) }
    fun toggleSetDropTargetHidden_noDropTarget() {
        runOnMainSync { controller.setDropTargetHidden(true) }
        waitForAnimateOut()
        assertThat(getDropTargetView()).isNull()
        runOnMainSync { controller.setHidden(false) }
        assertThat(dropTargetView).isNull()

        runOnMainSync { controller.setDropTargetHidden(false) }
        waitForAnimateIn()
        assertThat(getDropTargetView()).isNull()
        assertThat(dropTargetView).isNull()
    }

    @Test
    fun dismiss_dropTargetShown_viewRemoved() {
        runOnMainSync { controller.show(BubbleBarLocation.LEFT) }
    fun onDragEnd_dropTargetExists() {
        runOnMainSync {
            controller.onDragStart(initialLocationOnLeft = false)
            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
        }
        waitForAnimateIn()
        assertThat(getDropTargetView()).isNotNull()
        runOnMainSync { controller.dismiss() }
        assertThat(dropTargetView).isNotNull()

        runOnMainSync { controller.onDragEnd() }
        waitForAnimateOut()
        assertThat(getDropTargetView()).isNull()
        assertThat(dropTargetView).isNull()
    }

    @Test
    fun dismiss_dropTargetNotShown_doesNothing() {
        runOnMainSync { controller.dismiss() }
    fun onDragEnd_noDropTarget() {
        runOnMainSync { controller.onDragEnd() }
        waitForAnimateOut()
        assertThat(getDropTargetView()).isNull()
        assertThat(dropTargetView).isNull()
    }

    private fun getDropTargetView(): View? = container.findViewById(R.id.bubble_bar_drop_target)
    private val dropTargetView: View?
        get() = container.findViewById(R.id.bubble_bar_drop_target)

    private fun getExpectedDropTargetBounds(onLeft: Boolean): Rect {
        val rect = Rect()
@@ -177,4 +253,11 @@ class BubbleBarDropTargetControllerTest {
        // Advance animator for on-device test
        runOnMainSync { animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION) }
    }

    internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
        val locationChanges = mutableListOf<BubbleBarLocation>()
        override fun onChange(location: BubbleBarLocation) {
            locationChanges.add(location)
        }
    }
}
+8 −58
Original line number Diff line number Diff line
@@ -17,12 +17,9 @@
package com.android.wm.shell.bubbles.bar

import android.annotation.SuppressLint
import android.graphics.RectF
import android.view.MotionEvent
import android.view.View
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.common.bubbles.DismissView
import com.android.wm.shell.common.bubbles.RelativeTouchListener
import com.android.wm.shell.common.magnetictarget.MagnetizedObject
@@ -34,6 +31,7 @@ class BubbleBarExpandedViewDragController(
    private val dismissView: DismissView,
    private val animationHelper: BubbleBarAnimationHelper,
    private val bubblePositioner: BubblePositioner,
    private val pinController: BubbleExpandedViewPinController,
    private val dragListener: DragListener
) {

@@ -45,8 +43,6 @@ class BubbleBarExpandedViewDragController(
    private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
        MagnetizedObject.magnetizeView(expandedView)
    private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
    private val dismissZoneHeight: Int
    private val dismissZoneWidth: Int

    init {
        magnetizedExpandedView.magnetListener = MagnetListener()
@@ -78,32 +74,10 @@ class BubbleBarExpandedViewDragController(
            }
            return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
        }

        dismissZoneHeight =
            dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_height)
        dismissZoneWidth =
            dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_width)
    }

    /** Listener to receive callback about dragging events */
    /** Listener to get notified about drag events */
    interface DragListener {
        /**
         * Bubble bar [BubbleBarLocation] has changed as a result of dragging the expanded view.
         *
         * Triggered when drag gesture passes the middle of the screen and before touch up. Can be
         * triggered multiple times per gesture.
         *
         * @param location new location of the bubble bar as a result of the ongoing drag operation
         */
        fun onLocationChanged(location: BubbleBarLocation)

        /**
         * Called when bubble bar is moved into or out of the dismiss target
         *
         * @param isStuck `true` if view is dragged inside dismiss target
         */
        fun onStuckToDismissChanged(isStuck: Boolean)

        /**
         * Bubble bar was released
         *
@@ -115,25 +89,11 @@ class BubbleBarExpandedViewDragController(
    private inner class HandleDragListener : RelativeTouchListener() {

        private var isMoving = false
        private var screenCenterX: Int = -1
        private var isOnLeft = false
        private val dismissZone = RectF()

        override fun onDown(v: View, ev: MotionEvent): Boolean {
            // While animating, don't allow new touch events
            if (expandedView.isAnimating) return false
            isOnLeft = bubblePositioner.isBubbleBarOnLeft

            val screenRect = bubblePositioner.screenRect
            screenCenterX = screenRect.centerX()
            val screenBottom = screenRect.bottom

            dismissZone.set(
                screenCenterX - dismissZoneWidth / 2f,
                (screenBottom - dismissZoneHeight).toFloat(),
                screenCenterX + dismissZoneHeight / 2f,
                screenBottom.toFloat()
            )
            pinController.onDragStart(bubblePositioner.isBubbleBarOnLeft)
            return true
        }

@@ -152,19 +112,7 @@ class BubbleBarExpandedViewDragController(
            expandedView.translationX = expandedViewInitialTranslationX + dx
            expandedView.translationY = expandedViewInitialTranslationY + dy
            dismissView.show()

            // Check if we are in the zone around dismiss view where drag can only lead to dismiss
            if (dismissZone.contains(ev.rawX, ev.rawY)) {
                return
            }

            if (isOnLeft && ev.rawX > screenCenterX) {
                isOnLeft = false
                dragListener.onLocationChanged(BubbleBarLocation.RIGHT)
            } else if (!isOnLeft && ev.rawX < screenCenterX) {
                isOnLeft = true
                dragListener.onLocationChanged(BubbleBarLocation.LEFT)
            }
            pinController.onDragUpdate(ev.rawX, ev.rawY)
        }

        override fun onUp(
@@ -188,6 +136,7 @@ class BubbleBarExpandedViewDragController(
        private fun finishDrag() {
            if (!isStuckToDismiss) {
                animationHelper.animateToRestPosition()
                pinController.onDragEnd()
                dragListener.onReleased(inDismiss = false)
                dismissView.hide()
            }
@@ -201,7 +150,7 @@ class BubbleBarExpandedViewDragController(
            draggedObject: MagnetizedObject<*>
        ) {
            isStuckToDismiss = true
            dragListener.onStuckToDismissChanged(isStuck = true)
            pinController.setDropTargetHidden(true)
        }

        override fun onUnstuckFromTarget(
@@ -213,7 +162,7 @@ class BubbleBarExpandedViewDragController(
        ) {
            isStuckToDismiss = false
            animationHelper.animateUnstuckFromDismissView(target)
            dragListener.onStuckToDismissChanged(isStuck = false)
            pinController.setDropTargetHidden(false)
        }

        override fun onReleasedInTarget(
@@ -221,6 +170,7 @@ class BubbleBarExpandedViewDragController(
            draggedObject: MagnetizedObject<*>
        ) {
            dragListener.onReleased(inDismiss = true)
            pinController.onDragEnd()
            dismissView.hide()
        }
    }
+10 −28
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
@@ -44,7 +42,6 @@ import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.bubbles.DismissView;

import kotlin.Unit;
@@ -71,7 +68,7 @@ public class BubbleBarLayerView extends FrameLayout
    private final BubbleBarAnimationHelper mAnimationHelper;
    private final BubbleEducationViewController mEducationViewController;
    private final View mScrimView;
    private final BubbleBarDropTargetController mDropTargetController;
    private final BubbleExpandedViewPinController mBubbleExpandedViewPinController;

    @Nullable
    private BubbleViewProvider mExpandedBubble;
@@ -116,7 +113,9 @@ public class BubbleBarLayerView extends FrameLayout

        setUpDismissView();

        mDropTargetController = new BubbleBarDropTargetController(context, this, mPositioner);
        mBubbleExpandedViewPinController = new BubbleExpandedViewPinController(
                context, this, mPositioner);
        mBubbleExpandedViewPinController.setListener(mBubbleController::setBubbleBarLocation);

        setOnClickListener(view -> hideMenuOrCollapse());
    }
@@ -207,12 +206,17 @@ public class BubbleBarLayerView extends FrameLayout
                }
            });

            DragListener dragListener = createDragListener();
            DragListener dragListener = inDismiss -> {
                if (inDismiss && mExpandedBubble != null) {
                    mBubbleController.dismissBubble(mExpandedBubble.getKey(), DISMISS_USER_GESTURE);
                }
            };
            mDragController = new BubbleBarExpandedViewDragController(
                    mExpandedView,
                    mDismissView,
                    mAnimationHelper,
                    mPositioner,
                    mBubbleExpandedViewPinController,
                    dragListener);

            addView(mExpandedView, new LayoutParams(width, height, Gravity.LEFT));
@@ -377,26 +381,4 @@ public class BubbleBarLayerView extends FrameLayout
        }
    }

    private DragListener createDragListener() {
        return new DragListener() {
            @Override
            public void onLocationChanged(@NonNull BubbleBarLocation location) {
                mBubbleController.setBubbleBarLocation(location);
                mDropTargetController.show(location);
            }

            @Override
            public void onStuckToDismissChanged(boolean isStuck) {
                mDropTargetController.setHidden(isStuck);
            }

            @Override
            public void onReleased(boolean inDismiss) {
                mDropTargetController.dismiss();
                if (inDismiss && mExpandedBubble != null) {
                    mBubbleController.dismissBubble(mExpandedBubble.getKey(), DISMISS_USER_GESTURE);
                }
            }
        };
    }
}
+109 −0

File changed and moved.

Preview size limit exceeded, changes collapsed.

+179 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading