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 android.support.v4.util.Preconditions.checkArgument; 19 import static android.support.v7.widget.RecyclerView.NO_POSITION; 20 import static com.android.documentsui.selection.Shared.DEBUG; 21 import static com.android.documentsui.selection.Shared.TAG; 22 23 import android.util.Log; 24 25 import com.android.documentsui.selection.DefaultSelectionHelper.RangeType; 26 27 /** 28 * Class providing support for managing range selections. 29 */ 30 final class Range { 31 32 private final Callbacks mCallbacks; 33 private final int mBegin; 34 private int mEnd = NO_POSITION; 35 Range(Callbacks callbacks, int begin)36 public Range(Callbacks callbacks, int begin) { 37 if (DEBUG) Log.d(TAG, "New Ranger created beginning @ " + begin); 38 mCallbacks = callbacks; 39 mBegin = begin; 40 } 41 extendSelection(int position, @RangeType int type)42 void extendSelection(int position, @RangeType int type) { 43 checkArgument(position != NO_POSITION, "Position cannot be NO_POSITION."); 44 45 if (mEnd == NO_POSITION || mEnd == mBegin) { 46 // Reset mEnd so it can be established in establishRange. 47 mEnd = NO_POSITION; 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 checkArgument(mEnd == NO_POSITION, "End has already been set."); 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 checkArgument(mEnd != NO_POSITION, "End must already be set."); 72 checkArgument(mBegin != mEnd, "Beging and end point to same position."); 73 74 if (position == mEnd) { 75 if (DEBUG) Log.v(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 selection. 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 mCallbacks.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 DefaultSelectionHelper#updateForRange(int, int , boolean, int)}. 146 */ 147 static abstract class Callbacks { updateForRange( int begin, int end, boolean selected, @RangeType int type)148 abstract void updateForRange( 149 int begin, int end, boolean selected, @RangeType int type); 150 } 151 } 152