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

Commit 9b38acc8 authored by Mina Granic's avatar Mina Granic
Browse files

Cleanup LetterboxScrollProcessor with @NonNull and similar.

Flag: com.android.window.flags.scrolling_from_letterbox
Test: atest FrameworksCoreTests:LetterboxScrollProcessorTest
Bug: 353697519
Change-Id: Ifc0b5381149457a77465f5b5eaf06172113d9847
parent 44cb1941
Loading
Loading
Loading
Loading
+16 −8
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package android.view;
package android.view;


import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;

import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
@@ -23,6 +25,8 @@ import android.os.Handler;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;


import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.List;
@@ -35,6 +39,7 @@ import java.util.Set;
 *
 *
 * @hide
 * @hide
 */
 */
@VisibleForTesting(visibility = PACKAGE)
public class LetterboxScrollProcessor {
public class LetterboxScrollProcessor {


    private enum LetterboxScrollState {
    private enum LetterboxScrollState {
@@ -53,6 +58,7 @@ public class LetterboxScrollProcessor {
    /** IDs of events generated from this class */
    /** IDs of events generated from this class */
    private final Set<Integer> mGeneratedEventIds = new HashSet<>();
    private final Set<Integer> mGeneratedEventIds = new HashSet<>();


    @VisibleForTesting(visibility = PACKAGE)
    public LetterboxScrollProcessor(@NonNull Context context, @Nullable Handler handler) {
    public LetterboxScrollProcessor(@NonNull Context context, @Nullable Handler handler) {
        mContext = context;
        mContext = context;
        mScrollDetector = new GestureDetector(context, new ScrollListener(), handler);
        mScrollDetector = new GestureDetector(context, new ScrollListener(), handler);
@@ -69,7 +75,9 @@ public class LetterboxScrollProcessor {
     * @return The list of adjusted events, or null if no adjustments are needed. The list is empty
     * @return The list of adjusted events, or null if no adjustments are needed. The list is empty
     * if the event should be ignored. Do not keep a reference to the output as the list is reused.
     * if the event should be ignored. Do not keep a reference to the output as the list is reused.
     */
     */
    public List<MotionEvent> processMotionEvent(MotionEvent motionEvent) {
    @Nullable
    @VisibleForTesting(visibility = PACKAGE)
    public List<MotionEvent> processMotionEvent(@NonNull MotionEvent motionEvent) {
        mProcessedEvents.clear();
        mProcessedEvents.clear();
        final Rect appBounds = getAppBounds();
        final Rect appBounds = getAppBounds();


@@ -124,11 +132,9 @@ public class LetterboxScrollProcessor {
            mState = LetterboxScrollState.AWAITING_GESTURE_START;
            mState = LetterboxScrollState.AWAITING_GESTURE_START;
        }
        }


        if (makeNoAdjustments) return null;
        return makeNoAdjustments ? null : mProcessedEvents;
        return mProcessedEvents;
    }
    }



    /**
    /**
     * Processes the InputEvent for compatibility before it is finished by calling
     * Processes the InputEvent for compatibility before it is finished by calling
     * InputEventReceiver#finishInputEvent().
     * InputEventReceiver#finishInputEvent().
@@ -136,11 +142,13 @@ public class LetterboxScrollProcessor {
     * @param motionEvent The MotionEvent to process.
     * @param motionEvent The MotionEvent to process.
     * @return The motionEvent to finish, or null if it should not be finished.
     * @return The motionEvent to finish, or null if it should not be finished.
     */
     */
    public InputEvent processMotionEventBeforeFinish(MotionEvent motionEvent) {
    @Nullable
        if (mGeneratedEventIds.remove(motionEvent.getId())) return null;
    @VisibleForTesting(visibility = PACKAGE)
        return motionEvent;
    public InputEvent processMotionEventBeforeFinish(@NonNull MotionEvent motionEvent) {
        return mGeneratedEventIds.remove(motionEvent.getId()) ? null : motionEvent;
    }
    }


    @NonNull
    private Rect getAppBounds() {
    private Rect getAppBounds() {
        return mContext.getResources().getConfiguration().windowConfiguration.getBounds();
        return mContext.getResources().getConfiguration().windowConfiguration.getBounds();
    }
    }
@@ -160,7 +168,7 @@ public class LetterboxScrollProcessor {
                || motionEvent.getRawY() >= appBounds.bottom;
                || motionEvent.getRawY() >= appBounds.bottom;
    }
    }


    private void applyOffset(MotionEvent event, Rect appBounds) {
    private void applyOffset(@NonNull MotionEvent event, @NonNull Rect appBounds) {
        float horizontalOffset = calculateOffset(event.getX(), appBounds.width());
        float horizontalOffset = calculateOffset(event.getX(), appBounds.width());
        float verticalOffset = calculateOffset(event.getY(), appBounds.height());
        float verticalOffset = calculateOffset(event.getY(), appBounds.height());
        // Apply the offset to the motion event so it is over the app's view.
        // Apply the offset to the motion event so it is over the app's view.
+43 −36
Original line number Original line Diff line number Diff line
@@ -38,7 +38,6 @@ import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;



import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;


@@ -55,7 +54,7 @@ public class LetterboxScrollProcessorTest {
    private LetterboxScrollProcessor mLetterboxScrollProcessor;
    private LetterboxScrollProcessor mLetterboxScrollProcessor;
    private Context mContext;
    private Context mContext;


    // Constant delta used when comparing coordinates (floats)
    // Constant delta used when comparing coordinates (floats).
    private static final float EPSILON = 0.1f;
    private static final float EPSILON = 0.1f;


    private static final Rect APP_BOUNDS =
    private static final Rect APP_BOUNDS =
@@ -68,19 +67,19 @@ public class LetterboxScrollProcessorTest {
        // Set app bounds as if it was letterboxed.
        // Set app bounds as if it was letterboxed.
        mContext.getResources().getConfiguration().windowConfiguration.setBounds(APP_BOUNDS);
        mContext.getResources().getConfiguration().windowConfiguration.setBounds(APP_BOUNDS);


        Handler handler = new Handler(Looper.getMainLooper());

        // Recreate to reset LetterboxScrollProcessor state.
        // Recreate to reset LetterboxScrollProcessor state.
        mLetterboxScrollProcessor = new LetterboxScrollProcessor(mContext, handler);
        mLetterboxScrollProcessor = new LetterboxScrollProcessor(mContext,
                new Handler(Looper.getMainLooper()));
    }
    }


    @Test
    @Test
    public void testGestureInBoundsHasNoAdjustments() {
    public void testGestureInBoundsHasNoAdjustments() {
        // Tap-like gesture in bounds (non-scroll).
        // Tap-like gesture in bounds (non-scroll).
        List<MotionEvent> tapGestureEvents = createTapGestureEvents(0f, 0f);
        final List<MotionEvent> tapGestureEvents = createTapGestureEvents(
                /* startX= */ 0f, /* startY= */ 0f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);
        final List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);


        // Ensure no changes are made to events after processing - event locations should not be
        // Ensure no changes are made to events after processing - event locations should not be
        // adjusted because the gesture started in the app's bounds (for all gestures).
        // adjusted because the gesture started in the app's bounds (for all gestures).
@@ -110,10 +109,11 @@ public class LetterboxScrollProcessorTest {
    @Test
    @Test
    public void testGestureOutsideBoundsIsIgnored() {
    public void testGestureOutsideBoundsIsIgnored() {
        // Tap-like gesture outside bounds (non-scroll).
        // Tap-like gesture outside bounds (non-scroll).
        List<MotionEvent> tapGestureEvents = createTapGestureEvents(-100f, -100f);
        final List<MotionEvent> tapGestureEvents = createTapGestureEvents(
                /* startX= */ -100f, /* startY= */ -100f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);
        final List<MotionEvent> processedEvents = processMotionEvents(tapGestureEvents);


        // All events should be ignored since it was a non-scroll gesture and out of bounds.
        // All events should be ignored since it was a non-scroll gesture and out of bounds.
        assertEquals(0, processedEvents.size());
        assertEquals(0, processedEvents.size());
@@ -122,10 +122,11 @@ public class LetterboxScrollProcessorTest {
    @Test
    @Test
    public void testScrollGestureInBoundsHasNoAdjustments() {
    public void testScrollGestureInBoundsHasNoAdjustments() {
        // Scroll gesture in bounds (non-scroll).
        // Scroll gesture in bounds (non-scroll).
        List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(0f, 0f);
        final List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(
                /* startX= */ 0f, /* startY= */ 0f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
        final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);


        // Ensure no changes are made to events after processing - event locations should not be
        // Ensure no changes are made to events after processing - event locations should not be
        // adjusted because the gesture started in the app's bounds (for all gestures).
        // adjusted because the gesture started in the app's bounds (for all gestures).
@@ -137,10 +138,11 @@ public class LetterboxScrollProcessorTest {
    @Test
    @Test
    public void testScrollGestureInBoundsThenLeavesBoundsHasNoAdjustments() {
    public void testScrollGestureInBoundsThenLeavesBoundsHasNoAdjustments() {
        // Scroll gesture in bounds (non-scroll) that moves out of bounds.
        // Scroll gesture in bounds (non-scroll) that moves out of bounds.
        List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(390f, 790f);
        final List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(
                /* startX= */ 390f, /* startY= */ 790f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
        final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);


        // Ensure no changes are made to events after processing - event locations should not be
        // Ensure no changes are made to events after processing - event locations should not be
        // adjusted because the gesture started in the app's bounds (for all gestures), even if it
        // adjusted because the gesture started in the app's bounds (for all gestures), even if it
@@ -153,7 +155,8 @@ public class LetterboxScrollProcessorTest {
    @Test
    @Test
    public void testScrollGestureOutsideBoundsIsStartedInBounds() {
    public void testScrollGestureOutsideBoundsIsStartedInBounds() {
        // Scroll gesture outside bounds.
        // Scroll gesture outside bounds.
        List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(-100f, 0f);
        List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(
                /* startX= */ -100f, /* startY= */ 0f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
        List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
@@ -163,9 +166,9 @@ public class LetterboxScrollProcessorTest {


        // Ensure offset ACTION_DOWN is first event received.
        // Ensure offset ACTION_DOWN is first event received.
        MotionEvent firstProcessedEvent = processedEvents.getFirst();
        MotionEvent firstProcessedEvent = processedEvents.getFirst();
        assertEquals(firstProcessedEvent.getAction(), ACTION_DOWN);
        assertEquals(ACTION_DOWN, firstProcessedEvent.getAction());
        assertEquals(firstProcessedEvent.getX(), 0, EPSILON);
        assertEquals(0, firstProcessedEvent.getX(), EPSILON);
        assertEquals(firstProcessedEvent.getY(), 0, EPSILON);
        assertEquals(0, firstProcessedEvent.getY(), EPSILON);
        // Ensure this event is not finished (because it was generated by LetterboxScrollProcessor).
        // Ensure this event is not finished (because it was generated by LetterboxScrollProcessor).
        assertNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(firstProcessedEvent));
        assertNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(firstProcessedEvent));
    }
    }
@@ -173,16 +176,17 @@ public class LetterboxScrollProcessorTest {
    @Test
    @Test
    public void testScrollGestureOutsideBoundsIsMovedInBounds() {
    public void testScrollGestureOutsideBoundsIsMovedInBounds() {
        // Scroll gesture outside bounds.
        // Scroll gesture outside bounds.
        List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(-100f, 0f);
        final List<MotionEvent> scrollGestureEvents = createScrollGestureEvents(
                /* startX= */ -100f, /* startY= */ 0f);


        // Get processed events from Letterbox Scroll Processor.
        // Get processed events from Letterbox Scroll Processor.
        List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);
        final List<MotionEvent> processedEvents = processMotionEvents(scrollGestureEvents);


        // When a scroll occurs outside bounds: once detected as a scroll, an offset ACTION_DOWN is
        // When a scroll occurs outside bounds: once detected as a scroll, an offset ACTION_DOWN is
        // placed and then the rest of the gesture is offset also. Some ACTION_MOVE events may be
        // placed and then the rest of the gesture is offset also. Some ACTION_MOVE events may be
        // ignored until the gesture is 'detected as a scroll'.
        // ignored until the gesture is 'detected as a scroll'.
        // For this test, we expect the first ACTION_MOVE event to be ignored:
        // For this test, we expect the first ACTION_MOVE event to be ignored:
        scrollGestureEvents.remove(1);
        scrollGestureEvents.remove(/* index= */ 1);


        // Ensure all processed events (that are not ignored) are offset over the app.
        // Ensure all processed events (that are not ignored) are offset over the app.
        assertXCoordinatesAdjustedToZero(scrollGestureEvents, processedEvents);
        assertXCoordinatesAdjustedToZero(scrollGestureEvents, processedEvents);
@@ -191,8 +195,9 @@ public class LetterboxScrollProcessorTest {
        assertMotionEventsShouldBeFinished(processedEvents.subList(1, processedEvents.size()));
        assertMotionEventsShouldBeFinished(processedEvents.subList(1, processedEvents.size()));
    }
    }


    private List<MotionEvent> processMotionEvents(List<MotionEvent> motionEvents) {
    @NonNull
        List<MotionEvent> processedEvents = new ArrayList<>();
    private List<MotionEvent> processMotionEvents(@NonNull List<MotionEvent> motionEvents) {
        final List<MotionEvent> processedEvents = new ArrayList<>();
        for (MotionEvent motionEvent : motionEvents) {
        for (MotionEvent motionEvent : motionEvents) {
            MotionEvent clonedEvent = MotionEvent.obtain(motionEvent);
            MotionEvent clonedEvent = MotionEvent.obtain(motionEvent);
            List<MotionEvent> letterboxScrollCompatEvents =
            List<MotionEvent> letterboxScrollCompatEvents =
@@ -212,6 +217,7 @@ public class LetterboxScrollProcessorTest {
     * Creates and returns a tap gesture with X and Y in reference to the app bounds (top left
     * Creates and returns a tap gesture with X and Y in reference to the app bounds (top left
     * corner is x=0, y=0).
     * corner is x=0, y=0).
     */
     */
    @NonNull
    private List<MotionEvent> createTapGestureEvents(float startX, float startY) {
    private List<MotionEvent> createTapGestureEvents(float startX, float startY) {
        return createTapGestureEventsWithCoordinateSystem(startX, startY, APP_BOUNDS);
        return createTapGestureEventsWithCoordinateSystem(startX, startY, APP_BOUNDS);
    }
    }
@@ -230,20 +236,21 @@ public class LetterboxScrollProcessorTest {
        return motionEvents;
        return motionEvents;
    }
    }


    @NonNull
    private List<MotionEvent> createScrollGestureEvents(float startX, float startY) {
    private List<MotionEvent> createScrollGestureEvents(float startX, float startY) {
        float touchSlop = (float) ViewConfiguration.get(mContext).getScaledTouchSlop();
        final float touchSlop = (float) ViewConfiguration.get(mContext).getScaledTouchSlop();


        // Events for scroll gesture (starts at (startX, startY) then moves down-right
        // Events for scroll gesture (starts at (startX, startY) then moves down-right.
        List<MotionEvent> motionEvents = new ArrayList<>();
        final List<MotionEvent> motionEvents = new ArrayList<>();
        motionEvents.add(createBasicMotionEvent(0, ACTION_DOWN, startX, startY));
        motionEvents.add(createBasicMotionEvent(/* downTime= */ 0, ACTION_DOWN, startX, startY));
        motionEvents.add(createBasicMotionEvent(10, ACTION_MOVE,
        motionEvents.add(createBasicMotionEvent(/* downTime= */ 10, ACTION_MOVE,
                startX + touchSlop / 2, startY + touchSlop / 2));
                startX + touchSlop / 2, startY + touchSlop / 2));
        // Below event is first event in the scroll gesture where distance > touchSlop
        // Below event is first event in the scroll gesture where distance > touchSlop.
        motionEvents.add(createBasicMotionEvent(20, ACTION_MOVE,
        motionEvents.add(createBasicMotionEvent(/* downTime= */ 20, ACTION_MOVE,
                startX + touchSlop * 2, startY + touchSlop * 2));
                startX + touchSlop * 2, startY + touchSlop * 2));
        motionEvents.add(createBasicMotionEvent(30, ACTION_MOVE,
        motionEvents.add(createBasicMotionEvent(/* downTime= */ 30, ACTION_MOVE,
                startX + touchSlop * 3, startY + touchSlop * 3));
                startX + touchSlop * 3, startY + touchSlop * 3));
        motionEvents.add(createBasicMotionEvent(40, ACTION_UP,
        motionEvents.add(createBasicMotionEvent(/* downTime= */ 40, ACTION_UP,
                startX + touchSlop * 3, startY + touchSlop * 3));
                startX + touchSlop * 3, startY + touchSlop * 3));
        return motionEvents;
        return motionEvents;
    }
    }
@@ -274,8 +281,8 @@ public class LetterboxScrollProcessorTest {
    }
    }


    private void assertEventLocationsAreNotAdjusted(
    private void assertEventLocationsAreNotAdjusted(
            List<MotionEvent> originalEvents,
            @NonNull List<MotionEvent> originalEvents,
            List<MotionEvent> processedEvents) {
            @NonNull List<MotionEvent> processedEvents) {
        assertEquals("MotionEvent arrays are not the same size",
        assertEquals("MotionEvent arrays are not the same size",
                originalEvents.size(), processedEvents.size());
                originalEvents.size(), processedEvents.size());


@@ -288,8 +295,8 @@ public class LetterboxScrollProcessorTest {
    }
    }


    private void assertXCoordinatesAdjustedToZero(
    private void assertXCoordinatesAdjustedToZero(
            List<MotionEvent> originalEvents,
            @NonNull List<MotionEvent> originalEvents,
            List<MotionEvent> processedEvents) {
            @NonNull List<MotionEvent> processedEvents) {
        assertEquals("MotionEvent arrays are not the same size",
        assertEquals("MotionEvent arrays are not the same size",
                originalEvents.size(), processedEvents.size());
                originalEvents.size(), processedEvents.size());


@@ -301,7 +308,7 @@ public class LetterboxScrollProcessorTest {
        }
        }
    }
    }


    private void assertMotionEventsShouldBeFinished(List<MotionEvent> processedEvents) {
    private void assertMotionEventsShouldBeFinished(@NonNull List<MotionEvent> processedEvents) {
        for (MotionEvent processedEvent : processedEvents) {
        for (MotionEvent processedEvent : processedEvents) {
            assertNotNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(processedEvent));
            assertNotNull(mLetterboxScrollProcessor.processMotionEventBeforeFinish(processedEvent));
        }
        }