/* * 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.selection; import static com.android.documentsui.base.Shared.DEBUG; import static com.android.documentsui.base.Shared.VERBOSE; import android.support.v7.widget.RecyclerView; import android.util.Log; import com.android.documentsui.selection.SelectionManager.RangeType; /** * Class providing support for managing range selections. */ final class Range { private static final int UNDEFINED = -1; private final Range.RangeUpdater mUpdater; private final int mBegin; private int mEnd = UNDEFINED; public Range(Range.RangeUpdater updater, int begin) { if (DEBUG) Log.d(SelectionManager.TAG, "New Ranger created beginning @ " + begin); mUpdater = updater; mBegin = begin; } void snapSelection(int position, @RangeType int type) { assert(position != RecyclerView.NO_POSITION); if (mEnd == UNDEFINED || mEnd == mBegin) { // Reset mEnd so it can be established in establishRange. mEnd = UNDEFINED; establishRange(position, type); } else { reviseRange(position, type); } } private void establishRange(int position, @RangeType int type) { assert(mEnd == UNDEFINED); if (position == mBegin) { mEnd = position; } if (position > mBegin) { updateRange(mBegin + 1, position, true, type); } else if (position < mBegin) { updateRange(position, mBegin - 1, true, type); } mEnd = position; } private void reviseRange(int position, @RangeType int type) { assert(mEnd != UNDEFINED); assert(mBegin != mEnd); if (position == mEnd) { if (VERBOSE) Log.v(SelectionManager.TAG, "Ignoring no-op revision for range: " + this); } if (mEnd > mBegin) { reviseAscendingRange(position, type); } else if (mEnd < mBegin) { reviseDescendingRange(position, type); } // the "else" case is covered by checkState at beginning of method. mEnd = position; } /** * Updates an existing ascending seleciton. * @param position */ private void reviseAscendingRange(int position, @RangeType int type) { // Reducing or reversing the range.... if (position < mEnd) { if (position < mBegin) { updateRange(mBegin + 1, mEnd, false, type); updateRange(position, mBegin -1, true, type); } else { updateRange(position + 1, mEnd, false, type); } } // Extending the range... else if (position > mEnd) { updateRange(mEnd + 1, position, true, type); } } private void reviseDescendingRange(int position, @RangeType int type) { // Reducing or reversing the range.... if (position > mEnd) { if (position > mBegin) { updateRange(mEnd, mBegin - 1, false, type); updateRange(mBegin + 1, position, true, type); } else { updateRange(mEnd, position - 1, false, type); } } // Extending the range... else if (position < mEnd) { updateRange(position, mEnd - 1, true, type); } } /** * Try to set selection state for all elements in range. Not that callbacks can cancel * selection of specific items, so some or even all items may not reflect the desired state * after the update is complete. * * @param begin Adapter position for range start (inclusive). * @param end Adapter position for range end (inclusive). * @param selected New selection state. */ private void updateRange(int begin, int end, boolean selected, @RangeType int type) { mUpdater.updateForRange(begin, end, selected, type); } @Override public String toString() { return "Range{begin=" + mBegin + ", end=" + mEnd + "}"; } /* * @see {@link MultiSelectManager#updateForRegularRange(int, int , boolean)} and {@link * MultiSelectManager#updateForProvisionalRange(int, int, boolean)} */ @FunctionalInterface interface RangeUpdater { void updateForRange(int begin, int end, boolean selected, @RangeType int type); } }