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

Commit 4a993b24 authored by Ben Kwa's avatar Ben Kwa Committed by Android (Google) Code Review
Browse files

Merge "Allow multiple range selections using the shift key." into nyc-dev

parents 844674f9 83df50f9
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -263,7 +263,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
        mSelectionManager.addCallback(selectionListener);

        // Make sure this is done after the RecyclerView is set up.
        mFocusManager = new FocusManager(mRecView, mSelectionManager);
        mFocusManager = new FocusManager(mRecView);

        mModel = new Model();
        mModel.addUpdateListener(mAdapter);
@@ -1261,6 +1261,18 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
            }

            if (mFocusManager.handleKey(doc, keyCode, event)) {
                // Handle range selection adjustments. Extending the selection will adjust the
                // bounds of the in-progress range selection. Each time an unshifted navigation
                // event is received, the range selection is restarted.
                if (shouldExtendSelection(event)) {
                    if (!mSelectionManager.isRangeSelectionActive()) {
                        // Start a range selection if one isn't active
                        mSelectionManager.startRangeSelection(doc.getAdapterPosition());
                    }
                    mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
                } else {
                    mSelectionManager.endRangeSelection();
                }
                return true;
            }

@@ -1275,6 +1287,11 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi

            return false;
        }

        private boolean shouldExtendSelection(KeyEvent event) {
            return Events.isNavigationKeyCode(event.getKeyCode()) &&
                    event.isShiftPressed();
        }
    }

    private final class ModelUpdateListener implements Model.UpdateListener {
+8 −10
Original line number Diff line number Diff line
@@ -33,15 +33,13 @@ class FocusManager implements View.OnFocusChangeListener {
    private RecyclerView mView;
    private RecyclerView.Adapter<?> mAdapter;
    private LinearLayoutManager mLayout;
    private MultiSelectManager mSelectionManager;

    private int mLastFocusPosition = RecyclerView.NO_POSITION;

    public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
    public FocusManager(RecyclerView view) {
        mView = view;
        mAdapter = view.getAdapter();
        mLayout = (LinearLayoutManager) view.getLayoutManager();
        mSelectionManager = selectionManager;
    }

    /**
@@ -72,13 +70,6 @@ class FocusManager implements View.OnFocusChangeListener {

            if (endPos != RecyclerView.NO_POSITION) {
                focusItem(endPos);
                boolean extendSelection = event.isShiftPressed();

                // Handle any necessary adjustments to selection.
                if (extendSelection) {
                    int startPos = doc.getAdapterPosition();
                    mSelectionManager.selectRange(startPos, endPos);
                }
            }
            // Swallow all navigation keystrokes. Otherwise they go to the app's global
            // key-handler, which will route them back to the DF and cause focus to be reset.
@@ -108,6 +99,13 @@ class FocusManager implements View.OnFocusChangeListener {
        }
    }

    /**
     * @return The adapter position of the last focused item.
     */
    public int getFocusPosition() {
        return mLastFocusPosition;
    }

    /**
     * Finds the destination position where the focus should land for a given navigation event.
     *
+26 −24
Original line number Diff line number Diff line
@@ -370,39 +370,41 @@ public final class MultiSelectManager {
    }

    /**
     * Handle a range selection event.
     * <li> If the MSM is currently in single-select mode, only the last item in the range will
     * actually be selected.
     * <li>If a range selection is not already active, one will be started, and the given range of
     * items will be selected.  The given startPos becomes the anchor for the range selection.
     * <li>If a range selection is already active, the anchor is not changed. The range is extended
     * from its current anchor to endPos.
     * Starts a range selection. If a range selection is already active, this will start a new range
     * selection (which will reset the range anchor).
     *
     * @param startPos
     * @param endPos
     * @param pos The anchor position for the selection range.
     */
    public void selectRange(int startPos, int endPos) {
        // In single-select mode, just select the last item in the range.
        if (mSingleSelect) {
            attemptSelect(mAdapter.getModelId(endPos));
            return;
    void startRangeSelection(int pos) {
      attemptSelect(mAdapter.getModelId(pos));
      setSelectionRangeBegin(pos);
    }

        // In regular (i.e. multi-select) mode
        if (!isRangeSelectionActive()) {
            // If a range selection isn't active, start one up
            attemptSelect(mAdapter.getModelId(startPos));
            setSelectionRangeBegin(startPos);
        }
        // Extend the range selection
        mRanger.snapSelection(endPos);
    /**
     * Sets the end point for the current range selection, started by a call to
     * {@link #startRangeSelection(int)}. This function should only be called when a range selection
     * is active (see {@link #isRangeSelectionActive()}. Items in the range [anchor, end] will be
     * selected.
     *
     * @param pos The new end position for the selection range.
     */
    void snapRangeSelection(int pos) {
        checkNotNull(mRanger);
        mRanger.snapSelection(pos);
        notifySelectionChanged();
    }

    /**
     * Stops an in-progress range selection.
     */
    void endRangeSelection() {
        mRanger = null;
    }

    /**
     * @return Whether or not there is a current range selection active.
     */
    private boolean isRangeSelectionActive() {
    boolean isRangeSelectionActive() {
        return mRanger != null;
    }

+48 −0
Original line number Diff line number Diff line
@@ -189,6 +189,54 @@ public class MultiSelectManagerTest extends AndroidTestCase {
        assertSelection(items.get(20));
    }

    public void testRangeSelection() {
        mManager.startRangeSelection(15);
        mManager.snapRangeSelection(19);
        assertRangeSelection(15, 19);
    }

    public void testRangeSelection_snapExpand() {
        mManager.startRangeSelection(15);
        mManager.snapRangeSelection(19);
        mManager.snapRangeSelection(27);
        assertRangeSelection(15, 27);
    }

    public void testRangeSelection_snapContract() {
        mManager.startRangeSelection(15);
        mManager.snapRangeSelection(27);
        mManager.snapRangeSelection(19);
        assertRangeSelection(15, 19);
    }

    public void testRangeSelection_snapInvert() {
        mManager.startRangeSelection(15);
        mManager.snapRangeSelection(27);
        mManager.snapRangeSelection(3);
        assertRangeSelection(3, 15);
    }

    public void testRangeSelection_multiple() {
        mManager.startRangeSelection(15);
        mManager.snapRangeSelection(27);
        mManager.endRangeSelection();
        mManager.startRangeSelection(42);
        mManager.snapRangeSelection(57);
        assertSelectionSize(29);
        assertRangeSelected(15, 27);
        assertRangeSelected(42, 57);

    }

    public void testRangeSelection_singleSelect() {
        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null);
        mManager.addCallback(mCallback);
        mManager.startRangeSelection(11);
        mManager.snapRangeSelection(19);
        assertSelectionSize(1);
        assertSelection(items.get(19));
    }

    public void testProvisionalSelection() {
        Selection s = mManager.getSelection();
        assertSelection();