blob: c7db84c48dc14a7f05e376de616abaf0b4414aff [file] [log] [blame]
/*
* 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);
}
}