1 /* 2 * Copyright (C) 2017 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 17 package com.android.documentsui.selection; 18 19 import java.util.List; 20 import java.util.Set; 21 22 /** 23 * SelectionManager provides support for managing selection within a RecyclerView instance. 24 * 25 * @see DefaultSelectionHelper for details on instantiation. 26 */ 27 public abstract class SelectionHelper { 28 29 /** 30 * This value is included in the payload when SelectionHelper implementations 31 * notify RecyclerView of changes. Clients can look for this in 32 * {@code onBindViewHolder} to know if the bind event is occurring in response 33 * to a selection state change. 34 */ 35 public static final String SELECTION_CHANGED_MARKER = "Selection-Changed"; 36 37 /** 38 * Adds {@code observe} to be notified when changes to selection occur. 39 * 40 * @param observer 41 */ 42 public abstract void addObserver(SelectionObserver observer); 43 44 public abstract boolean hasSelection(); 45 46 /** 47 * Returns a Selection object that provides a live view on the current selection. 48 * 49 * @see #copySelection(Selection) on how to get a snapshot 50 * of the selection that will not reflect future changes 51 * to selection. 52 * 53 * @return The current selection. 54 */ 55 public abstract Selection getSelection(); 56 57 /** 58 * Updates {@code dest} to reflect the current selection. 59 * @param dest 60 */ 61 public abstract void copySelection(Selection dest); 62 63 /** 64 * @return true if the item specified by its id is selected. Shorthand for 65 * {@code getSelection().contains(String)}. 66 */ 67 public abstract boolean isSelected(String id); 68 69 /** 70 * Restores the selected state of specified items. Used in cases such as restore the selection 71 * after rotation etc. Provisional selection, being provisional 'n all, isn't restored. 72 */ 73 public abstract void restoreSelection(Selection other); 74 75 /** 76 * Sets the selected state of the specified items. Note that the callback will NOT 77 * be consulted to see if an item can be selected. 78 * 79 * @param ids 80 * @param selected 81 * @return 82 */ 83 public abstract boolean setItemsSelected(Iterable<String> ids, boolean selected); 84 85 /** 86 * Clears the selection and notifies (if something changes). 87 */ 88 public abstract void clearSelection(); 89 90 /** 91 * Attempts to select an item. 92 * 93 * @return true if the item was selected. False if the item was not selected, or was 94 * was already selected prior to the method being called. 95 */ 96 public abstract boolean select(String itemId); 97 98 /** 99 * Attempts to deselect an item. 100 * 101 * @return true if the item was deselected. False if the item was not deselected, or was 102 * was already deselected prior to the method being called. 103 */ 104 public abstract boolean deselect(String itemId); 105 106 /** 107 * Starts a range selection. If a range selection is already active, this will start a new range 108 * selection (which will reset the range anchor). 109 * 110 * @param pos The anchor position for the selection range. 111 */ 112 public abstract void startRange(int pos); 113 114 /** 115 * Sets the end point for the active range selection. 116 * 117 * <p>This function should only be called when a range selection is active 118 * (see {@link #isRangeActive()}. Items in the range [anchor, end] will be 119 * selected. 120 * 121 * @param pos The new end position for the selection range. 122 * @param type The type of selection the range should utilize. 123 * 124 * @throws IllegalStateException if a range selection is not active. Range selection 125 * must have been started by a call to {@link #startRange(int)}. 126 */ 127 public abstract void extendRange(int pos); 128 129 /** 130 * Stops an in-progress range selection. All selection done with 131 * {@link #extendProvisionalRange(int)} will be lost if 132 * {@link Selection#mergeProvisionalSelection()} is not called beforehand. 133 */ 134 public abstract void endRange(); 135 136 /** 137 * @return Whether or not there is a current range selection active. 138 */ 139 public abstract boolean isRangeActive(); 140 141 /** 142 * Sets the magic location at which a selection range begins (the selection anchor). This value 143 * is consulted when determining how to extend, and modify selection ranges. Calling this when a 144 * range selection is active will reset the range selection. 145 */ 146 public abstract void anchorRange(int position); 147 148 /** 149 * @param pos 150 */ 151 // TODO: This is smelly. Maybe this type of logic needs to move into range selection, 152 // then selection manager can have a startProvisionalRange and startRange. Or 153 // maybe ranges always start life as provisional. 154 public abstract void extendProvisionalRange(int pos); 155 156 /** 157 * @param newSelection 158 */ 159 public abstract void setProvisionalSelection(Set<String> newSelection); 160 161 /** 162 * 163 */ 164 public abstract void clearProvisionalSelection(); 165 166 public abstract void mergeProvisionalSelection(); 167 168 /** 169 * Observer interface providing access to information about Selection state changes. 170 */ 171 public static abstract class SelectionObserver { 172 173 /** 174 * Called when state of an item has been changed. 175 */ 176 public void onItemStateChanged(String id, boolean selected) {} 177 178 /** 179 * Called when the underlying data set has change. After this method is called 180 * the selection manager will attempt traverse the existing selection, 181 * calling {@link #onItemStateChanged(String, boolean)} for each selected item, 182 * and deselecting any items that cannot be selected given the updated dataset. 183 */ 184 public void onSelectionReset() {} 185 186 /** 187 * Called immediately after completion of any set of changes, excluding 188 * those resulting in calls to {@link #onSelectionReset()} and 189 * {@link #onSelectionRestored()}. 190 */ 191 public void onSelectionChanged() {} 192 193 /** 194 * Called immediately after selection is restored. 195 * {@link #onItemStateChanged(String, boolean)} will not be called 196 * for individual items in the selection. 197 */ 198 public void onSelectionRestored() {} 199 } 200 201 /** 202 * Facilitates the use of stable ids. 203 */ 204 public static abstract class StableIdProvider { 205 /** 206 * @return The model ID of the item at the given adapter position. 207 */ 208 public abstract String getStableId(int position); 209 210 /** 211 * @return the position of a stable ID, or RecyclerView.NO_POSITION. 212 */ 213 public abstract int getPosition(String id); 214 215 /** 216 * @return A list of all known stable IDs. 217 */ 218 public abstract List<String> getStableIds(); 219 } 220 221 /** 222 * Implement SelectionPredicate to control when items can be selected or unselected. 223 */ 224 public static abstract class SelectionPredicate { 225 226 /** @return true if the item at {@code id} can be set to {@code nextState}. */ 227 public abstract boolean canSetStateForId(String id, boolean nextState); 228 229 /** @return true if the item at {@code id} can be set to {@code nextState}. */ 230 public abstract boolean canSetStateAtPosition(int position, boolean nextState); 231 232 /** @return true if more than a single item can be selected. */ 233 public boolean canSelectMultiple() { 234 return true; 235 } 236 } 237 } 238