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

Commit abbb6e0b authored by Ben Lin's avatar Ben Lin
Browse files

Fix Gesture Select bug when user is in RTL mode.

For Gesture Select, we wanted to ensure if user ever moves beyond the
last item, we select everything up to the last item. This was assuming
the "blank area" is always to the right of the last item, which is not
true for RTL.

Bug: 31064020
Change-Id: I8d70b29c15b059b692c72a86fdb765305dd8dbfc
parent 837b7ead
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.documentsui.dirlist;

import android.graphics.Point;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.View;
@@ -189,7 +190,12 @@ final class GestureSelector {
            // last item of the recycler view), we would want to set that as the currentItemPos
            View lastItem = rv.getLayoutManager()
                    .getChildAt(rv.getLayoutManager().getChildCount() - 1);
            boolean bottomRight = e.getX() > lastItem.getRight() && e.getY() > lastItem.getTop();
            int direction = rv.getContext().getResources().getConfiguration().getLayoutDirection();
            final boolean pastLastItem = isPastLastItem(lastItem.getTop(),
                    lastItem.getLeft(),
                    lastItem.getRight(),
                    event,
                    direction);

            // Since views get attached & detached from RecyclerView,
            // {@link LayoutManager#getChildCount} can return a different number from the actual
@@ -197,7 +203,7 @@ final class GestureSelector {
            // of items in the adapter. Using the adapter is the for sure way to get the actual last
            // item position.
            final float inboundY = getInboundY(rv.getHeight(), e.getY());
            final int lastGlidedItemPos = (bottomRight) ? rv.getAdapter().getItemCount() - 1
            final int lastGlidedItemPos = (pastLastItem) ? rv.getAdapter().getItemCount() - 1
                    : rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(), inboundY));
            if (lastGlidedItemPos != RecyclerView.NO_POSITION) {
                doGestureMultiSelect(lastGlidedItemPos);
@@ -211,7 +217,7 @@ final class GestureSelector {
    // It's possible for events to go over the top/bottom of the RecyclerView.
    // We want to get a Y-coordinate within the RecyclerView so we can find the childView underneath
    // correctly.
    private float getInboundY(float max, float y) {
    private static float getInboundY(float max, float y) {
        if (y < 0f) {
            return 0f;
        } else if (y > max) {
@@ -220,6 +226,20 @@ final class GestureSelector {
        return y;
    }

    /*
     * Check to see an InputEvent if past a particular item, i.e. to the right or to the bottom
     * of the item.
     * For RTL, it would to be to the left or to the bottom of the item.
     */
    @VisibleForTesting
    static boolean isPastLastItem(int top, int left, int right, InputEvent e, int direction) {
        if (direction == View.LAYOUT_DIRECTION_LTR) {
            return e.getX() > right && e.getY() > top;
        } else {
            return e.getX() < left && e.getY() > top;
        }
    }

    /* Given the end position, select everything in-between.
     * @param endPos  The adapter position of the end item.
     */
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.documentsui.dirlist;

import android.graphics.Point;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;

import com.android.documentsui.TestInputEvent;

@SmallTest
public class GestureSelectorTest extends AndroidTestCase {

    TestInputEvent e;

    // Simulate a (20, 20) box locating at (20, 20)
    static final int LEFT_BORDER = 20;
    static final int RIGHT_BORDER = 40;
    static final int TOP_BORDER = 20;
    static final int BOTTOM_BORDER = 40;

    @Override
    public void setUp() throws Exception {
        e = new TestInputEvent();
    }

    public void testLTRPastLastItem() {
        e.location = new Point(100, 100);
        assertTrue(GestureSelector.isPastLastItem(
                TOP_BORDER, LEFT_BORDER, RIGHT_BORDER, e, View.LAYOUT_DIRECTION_LTR));
    }

    public void testLTRPastLastItem_Inverse() {
        e.location = new Point(10, 10);
        assertFalse(GestureSelector.isPastLastItem(
                TOP_BORDER, LEFT_BORDER, RIGHT_BORDER, e, View.LAYOUT_DIRECTION_LTR));
    }

    public void testRTLPastLastItem() {
        e.location = new Point(10, 30);
        assertTrue(GestureSelector.isPastLastItem(
                TOP_BORDER, LEFT_BORDER, RIGHT_BORDER, e, View.LAYOUT_DIRECTION_RTL));
    }

    public void testRTLPastLastItem_Inverse() {
        e.location = new Point(100, 100);
        assertFalse(GestureSelector.isPastLastItem(
                TOP_BORDER, LEFT_BORDER, RIGHT_BORDER, e, View.LAYOUT_DIRECTION_RTL));
    }
}