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

Commit 1b924ffc authored by Prince's avatar Prince
Browse files

Added exclusions and calculated a default bouncer region

This change examines a list of exclusionRects, these rectangles might represent area where another application is overlaying content and wants
to reduce the area to trigger the bouncer's swipe trigger, if an exclusion rectangle overlaps with the default bouncer region, the method
will shrink bouncer region

Flag: NA
Test: atest DreamOverlayTouchMonitorTest, BouncerSwipeTouchHandlerTest
Fixes: 327003425

Change-Id: I83b82168354a3da134af658ee672d4ae343e8d97
parent 52d43aed
Loading
Loading
Loading
Loading
+83 −3
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ import org.mockito.MockitoAnnotations;

import java.util.Collections;
import java.util.Optional;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@@ -119,6 +118,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
    private static final float TOUCH_REGION = .3f;
    private static final int SCREEN_WIDTH_PX = 1024;
    private static final int SCREEN_HEIGHT_PX = 100;
    private static final float MIN_BOUNCER_HEIGHT = .05f;

    private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
    private static final UserInfo CURRENT_USER_INFO = new UserInfo(
@@ -142,6 +142,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
                mFlingAnimationUtils,
                mFlingAnimationUtilsClosing,
                TOUCH_REGION,
                MIN_BOUNCER_HEIGHT,
                mUiEventLogger);

        when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
@@ -160,9 +161,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
     */
    @Test
    public void testSessionStart() {
        mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion);
        mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, null);

        verify(mRegion).op(mRectCaptor.capture(), eq(Region.Op.UNION));
        verify(mRegion).union(mRectCaptor.capture());
        final Rect bounds = mRectCaptor.getValue();

        final Rect expected = new Rect();
@@ -194,6 +195,85 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
        UP,
    }

    @Test
    public void testSwipeUp_whenBouncerInitiallyShowing_reduceHeightWithExclusionRects() {
        mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion,
                new Rect(0, 0, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX));
        verify(mRegion).union(mRectCaptor.capture());
        final Rect bounds = mRectCaptor.getValue();

        final Rect expected = new Rect();
        final float minBouncerHeight =
                SCREEN_HEIGHT_PX * MIN_BOUNCER_HEIGHT;
        final int minAllowableBottom = SCREEN_HEIGHT_PX - Math.round(minBouncerHeight);

        expected.set(0, minAllowableBottom , SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX);

        assertThat(bounds).isEqualTo(expected);

        onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController);
    }

    @Test
    public void testSwipeUp_exclusionRectAtTop_doesNotIntersectGestureArea() {
        mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion,
                new Rect(0, 0, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX / 4));
        verify(mRegion).union(mRectCaptor.capture());
        final Rect bounds = mRectCaptor.getValue();

        final Rect expected = new Rect();
        final int gestureAreaTop = SCREEN_HEIGHT_PX - Math.round(SCREEN_HEIGHT_PX * TOUCH_REGION);
        expected.set(0, gestureAreaTop, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX);

        assertThat(bounds).isEqualTo(expected);
        onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController);
    }

    @Test
    public void testSwipeUp_exclusionRectBetweenNormalAndMinimumSwipeArea() {
        final int normalSwipeAreaTop = SCREEN_HEIGHT_PX
                - Math.round(SCREEN_HEIGHT_PX * TOUCH_REGION);
        final int minimumSwipeAreaTop = SCREEN_HEIGHT_PX
                - Math.round(SCREEN_HEIGHT_PX * MIN_BOUNCER_HEIGHT);

        Rect exclusionRect = new Rect(0, 0, SCREEN_WIDTH_PX,
                (normalSwipeAreaTop + minimumSwipeAreaTop) / 2);

        mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, exclusionRect);

        verify(mRegion).union(mRectCaptor.capture());

        final Rect bounds = mRectCaptor.getValue();
        final Rect expected = new Rect();

        final int expectedSwipeAreaBottom = exclusionRect.bottom;
        expected.set(0, expectedSwipeAreaBottom, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX);

        assertThat(bounds).isEqualTo(expected);

        onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController);
    }

    private static void onSessionStartHelper(BouncerSwipeTouchHandler touchHandler,
            DreamTouchHandler.TouchSession touchSession,
            NotificationShadeWindowController notificationShadeWindowController) {
        touchHandler.onSessionStart(touchSession);
        verify(notificationShadeWindowController).setForcePluginOpen(eq(true), any());
        ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
                ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
        ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
        verify(touchSession).registerGestureListener(gestureListenerCaptor.capture());
        verify(touchSession).registerInputListener(eventListenerCaptor.capture());

        // A touch within range at the bottom of the screen should trigger listening
        assertThat(gestureListenerCaptor.getValue()
                .onScroll(Mockito.mock(MotionEvent.class),
                        Mockito.mock(MotionEvent.class),
                        1,
                        2)).isTrue();
    }

    /**
     * Makes sure swiping up when bouncer initially showing doesn't change the expansion amount.
     */
+4 −0
Original line number Diff line number Diff line
@@ -1863,6 +1863,10 @@
        .2
    </item>

    <item name="dream_overlay_bouncer_min_region_screen_percentage" format="float" type="dimen">
        .05
    </item>

    <!-- The padding applied to the dream overlay container -->
    <dimen name="dream_overlay_container_padding_start">0dp</dimen>
    <dimen name="dream_overlay_container_padding_end">0dp</dimen>
+20 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.dreams.touch;
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING;
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING;
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -81,6 +82,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
    private final LockPatternUtils mLockPatternUtils;
    private final UserTracker mUserTracker;
    private final float mBouncerZoneScreenPercentage;
    private final float mMinBouncerZoneScreenPercentage;

    private final ScrimManager mScrimManager;
    private ScrimController mCurrentScrimController;
@@ -223,6 +225,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
            @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
                    FlingAnimationUtils flingAnimationUtilsClosing,
            @Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage,
            @Named(MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE) float minRegionPercentage,
            UiEventLogger uiEventLogger) {
        mCentralSurfaces = centralSurfaces;
        mScrimManager = scrimManager;
@@ -230,6 +233,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
        mLockPatternUtils = lockPatternUtils;
        mUserTracker = userTracker;
        mBouncerZoneScreenPercentage = swipeRegionPercentage;
        mMinBouncerZoneScreenPercentage = minRegionPercentage;
        mFlingAnimationUtils = flingAnimationUtils;
        mFlingAnimationUtilsClosing = flingAnimationUtilsClosing;
        mValueAnimatorCreator = valueAnimatorCreator;
@@ -238,24 +242,27 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
    }

    @Override
    public void getTouchInitiationRegion(Rect bounds, Region region) {
    public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
        final int width = bounds.width();
        final int height = bounds.height();
        final float minBouncerHeight = height * mMinBouncerZoneScreenPercentage;
        final int minAllowableBottom = Math.round(height * (1 - mMinBouncerZoneScreenPercentage));

        if (mCentralSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
            region.op(new Rect(0, 0, width,
                            Math.round(
                                    height * mBouncerZoneScreenPercentage)),
                    Region.Op.UNION);
        } else {
            region.op(new Rect(0,
                            Math.round(height * (1 - mBouncerZoneScreenPercentage)),
                            width,
                            height),
                    Region.Op.UNION);
        final boolean isBouncerShowing =
                mCentralSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false);
        final Rect normalRegion = isBouncerShowing
                ? new Rect(0, 0, width, Math.round(height * mBouncerZoneScreenPercentage))
                : new Rect(0, Math.round(height * (1 - mBouncerZoneScreenPercentage)),
                        width, height);

        if (!isBouncerShowing && exclusionRect != null) {
            int lowestBottom = Math.min(Math.max(0, exclusionRect.bottom), minAllowableBottom);
            normalRegion.top = Math.max(normalRegion.top, lowestBottom);
        }
        region.union(normalRegion);
    }


    @Override
    public void onSessionStart(TouchSession session) {
        mVelocityTracker = mVelocityTrackerFactory.obtain();
+1 −1
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ public class CommunalTouchHandler implements DreamTouchHandler {
    }

    @Override
    public void getTouchInitiationRegion(Rect bounds, Region region) {
    public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
        final Rect outBounds = new Rect(bounds);
        outBounds.inset(outBounds.width() - mInitiationWidth, 0, 0, 0);
        region.op(outBounds, Region.Op.UNION);
+73 −11
Original line number Diff line number Diff line
@@ -18,9 +18,15 @@ package com.android.systemui.dreams.touch;

import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import static com.android.systemui.shared.Flags.bouncerAreaExclusion;

import android.graphics.Rect;
import android.graphics.Region;
import android.os.RemoteException;
import android.util.Log;
import android.view.GestureDetector;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InputEvent;
import android.view.MotionEvent;

@@ -31,6 +37,8 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;

import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -58,8 +66,23 @@ import javax.inject.Inject;
public class DreamOverlayTouchMonitor {
    // This executor is used to protect {@code mActiveTouchSessions} from being modified
    // concurrently. Any operation that adds or removes values should use this executor.
    private final Executor mExecutor;
    public String TAG = "DreamOverlayTouchMonitor";
    private final Executor mMainExecutor;
    private final Executor mBackgroundExecutor;
    private final Lifecycle mLifecycle;
    private Rect mExclusionRect = null;

    private ISystemGestureExclusionListener mGestureExclusionListener =
            new ISystemGestureExclusionListener.Stub() {
                @Override
                public void onSystemGestureExclusionChanged(int displayId,
                        Region systemGestureExclusion,
                        Region systemGestureExclusionUnrestricted) {
                    mExclusionRect = systemGestureExclusion.getBounds();
                }
            };



    /**
     * Adds a new {@link TouchSessionImpl} to participate in receiving future touches and gestures.
@@ -67,7 +90,7 @@ public class DreamOverlayTouchMonitor {
    private ListenableFuture<DreamTouchHandler.TouchSession> push(
            TouchSessionImpl touchSessionImpl) {
        return CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> {
            mMainExecutor.execute(() -> {
                if (!mActiveTouchSessions.remove(touchSessionImpl)) {
                    completer.set(null);
                    return;
@@ -90,7 +113,7 @@ public class DreamOverlayTouchMonitor {
    private ListenableFuture<DreamTouchHandler.TouchSession> pop(
            TouchSessionImpl touchSessionImpl) {
        return CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> {
            mMainExecutor.execute(() -> {
                if (mActiveTouchSessions.remove(touchSessionImpl)) {
                    touchSessionImpl.onRemoved();

@@ -240,6 +263,17 @@ public class DreamOverlayTouchMonitor {
     */
    private void startMonitoring() {
        stopMonitoring(true);
        if (bouncerAreaExclusion()) {
            mBackgroundExecutor.execute(() -> {
                try {
                    mWindowManagerService.registerSystemGestureExclusionListener(
                            mGestureExclusionListener, mDisplayId);
                } catch (RemoteException e) {
                    // Handle the exception
                    Log.e(TAG, "Failed to register gesture exclusion listener", e);
                }
            });
        }
        mCurrentInputSession = mInputSessionFactory.create(
                "dreamOverlay",
                mInputEventListener,
@@ -252,6 +286,18 @@ public class DreamOverlayTouchMonitor {
     * Destroys any active {@link InputSession}.
     */
    private void stopMonitoring(boolean force) {
        mExclusionRect = null;
        if (bouncerAreaExclusion()) {
            mBackgroundExecutor.execute(() -> {
                try {
                    mWindowManagerService.unregisterSystemGestureExclusionListener(
                            mGestureExclusionListener, mDisplayId);
                } catch (RemoteException e) {
                    // Handle the exception
                    Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
                }
            });
        }
        if (mCurrentInputSession == null) {
            return;
        }
@@ -263,7 +309,7 @@ public class DreamOverlayTouchMonitor {

        // When we stop monitoring touches, we must ensure that all active touch sessions and
        // descendants informed of the removal so any cleanup for active tracking can proceed.
        mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
        mMainExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
            while (touchSession != null) {
                touchSession.onRemoved();
                touchSession = touchSession.getPredecessor();
@@ -297,9 +343,13 @@ public class DreamOverlayTouchMonitor {
                            }
                            final Rect maxBounds = mDisplayHelper.getMaxBounds(ev.getDisplayId(),
                                    TYPE_APPLICATION_OVERLAY);

                            final Region initiationRegion = Region.obtain();
                    handler.getTouchInitiationRegion(maxBounds, initiationRegion);
                            Rect exclusionRect = null;
                            if (bouncerAreaExclusion()) {
                                exclusionRect = getCurrentExclusionRect();
                            }
                            handler.getTouchInitiationRegion(
                                            maxBounds, initiationRegion, exclusionRect);

                    if (!initiationRegion.isEmpty()) {
                        // Initiation regions require a motion event to determine pointer location
@@ -335,6 +385,9 @@ public class DreamOverlayTouchMonitor {
                    .flatMap(Collection::stream)
                    .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
        }
                    private Rect getCurrentExclusionRect() {
                        return mExclusionRect;
                    }
    };

    /**
@@ -416,6 +469,9 @@ public class DreamOverlayTouchMonitor {

    private InputSessionComponent.Factory mInputSessionFactory;
    private InputSession mCurrentInputSession;
    private final int mDisplayId;
    private final IWindowManager mWindowManagerService;


    /**
     * Designated constructor for {@link DreamOverlayTouchMonitor}
@@ -432,15 +488,21 @@ public class DreamOverlayTouchMonitor {
    @Inject
    public DreamOverlayTouchMonitor(
            @Main Executor executor,
            @Background Executor backgroundExecutor,
            Lifecycle lifecycle,
            InputSessionComponent.Factory inputSessionFactory,
            DisplayHelper displayHelper,
            Set<DreamTouchHandler> handlers) {
            Set<DreamTouchHandler> handlers,
            IWindowManager windowManagerService,
            @DisplayId int displayId) {
        mDisplayId = displayId;
        mHandlers = handlers;
        mInputSessionFactory = inputSessionFactory;
        mExecutor = executor;
        mMainExecutor = executor;
        mBackgroundExecutor = backgroundExecutor;
        mLifecycle = lifecycle;
        mDisplayHelper = displayHelper;
        mWindowManagerService = windowManagerService;
    }

    /**
Loading