1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.documentsui.selection; 17 18 import static com.android.documentsui.base.Shared.DEBUG; 19 import static com.android.documentsui.base.Shared.VERBOSE; 20 21 import android.support.v7.widget.RecyclerView; 22 import android.util.Log; 23 24 import com.android.documentsui.selection.SelectionManager.RangeType; 25 26 /** 27 * Class providing support for managing range selections. 28 */ 29 final class Range { 30 private static final int UNDEFINED = -1; 31 32 private final Range.RangeUpdater mUpdater; 33 private final int mBegin; 34 private int mEnd = UNDEFINED; 35 Range(Range.RangeUpdater updater, int begin)36 public Range(Range.RangeUpdater updater, int begin) { 37 if (DEBUG) Log.d(SelectionManager.TAG, "New Ranger created beginning @ " + begin); 38 mUpdater = updater; 39 mBegin = begin; 40 } 41 snapSelection(int position, @RangeType int type)42 void snapSelection(int position, @RangeType int type) { 43 assert(position != RecyclerView.NO_POSITION); 44 45 if (mEnd == UNDEFINED || mEnd == mBegin) { 46 // Reset mEnd so it can be established in establishRange. 47 mEnd = UNDEFINED; 48 establishRange(position, type); 49 } else { 50 reviseRange(position, type); 51 } 52 } 53 establishRange(int position, @RangeType int type)54 private void establishRange(int position, @RangeType int type) { 55 assert(mEnd == UNDEFINED); 56 57 if (position == mBegin) { 58 mEnd = position; 59 } 60 61 if (position > mBegin) { 62 updateRange(mBegin + 1, position, true, type); 63 } else if (position < mBegin) { 64 updateRange(position, mBegin - 1, true, type); 65 } 66 67 mEnd = position; 68 } 69 reviseRange(int position, @RangeType int type)70 private void reviseRange(int position, @RangeType int type) { 71 assert(mEnd != UNDEFINED); 72 assert(mBegin != mEnd); 73 74 if (position == mEnd) { 75 if (VERBOSE) Log.v(SelectionManager.TAG, "Ignoring no-op revision for range: " + this); 76 } 77 78 if (mEnd > mBegin) { 79 reviseAscendingRange(position, type); 80 } else if (mEnd < mBegin) { 81 reviseDescendingRange(position, type); 82 } 83 // the "else" case is covered by checkState at beginning of method. 84 85 mEnd = position; 86 } 87 88 /** 89 * Updates an existing ascending seleciton. 90 * @param position 91 */ reviseAscendingRange(int position, @RangeType int type)92 private void reviseAscendingRange(int position, @RangeType int type) { 93 // Reducing or reversing the range.... 94 if (position < mEnd) { 95 if (position < mBegin) { 96 updateRange(mBegin + 1, mEnd, false, type); 97 updateRange(position, mBegin -1, true, type); 98 } else { 99 updateRange(position + 1, mEnd, false, type); 100 } 101 } 102 103 // Extending the range... 104 else if (position > mEnd) { 105 updateRange(mEnd + 1, position, true, type); 106 } 107 } 108 reviseDescendingRange(int position, @RangeType int type)109 private void reviseDescendingRange(int position, @RangeType int type) { 110 // Reducing or reversing the range.... 111 if (position > mEnd) { 112 if (position > mBegin) { 113 updateRange(mEnd, mBegin - 1, false, type); 114 updateRange(mBegin + 1, position, true, type); 115 } else { 116 updateRange(mEnd, position - 1, false, type); 117 } 118 } 119 120 // Extending the range... 121 else if (position < mEnd) { 122 updateRange(position, mEnd - 1, true, type); 123 } 124 } 125 126 /** 127 * Try to set selection state for all elements in range. Not that callbacks can cancel 128 * selection of specific items, so some or even all items may not reflect the desired state 129 * after the update is complete. 130 * 131 * @param begin Adapter position for range start (inclusive). 132 * @param end Adapter position for range end (inclusive). 133 * @param selected New selection state. 134 */ updateRange(int begin, int end, boolean selected, @RangeType int type)135 private void updateRange(int begin, int end, boolean selected, @RangeType int type) { 136 mUpdater.updateForRange(begin, end, selected, type); 137 } 138 139 @Override toString()140 public String toString() { 141 return "Range{begin=" + mBegin + ", end=" + mEnd + "}"; 142 } 143 144 /* 145 * @see {@link MultiSelectManager#updateForRegularRange(int, int , boolean)} and {@link 146 * MultiSelectManager#updateForProvisionalRange(int, int, boolean)} 147 */ 148 @FunctionalInterface 149 interface RangeUpdater { updateForRange(int begin, int end, boolean selected, @RangeType int type)150 void updateForRange(int begin, int end, boolean selected, @RangeType int type); 151 } 152 } 153