| /* |
| * Copyright 2017 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 androidx.widget.recyclerview.selection; |
| |
| import static android.support.v4.util.Preconditions.checkArgument; |
| |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| import android.support.annotation.DrawableRes; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| import android.support.v7.widget.RecyclerView; |
| import android.view.GestureDetector; |
| import android.view.HapticFeedbackConstants; |
| import android.view.MotionEvent; |
| |
| import java.util.Set; |
| |
| /** |
| * SelectionTracker provides support for managing a selection of items in a RecyclerView instance. |
| * |
| * <p> |
| * This class provides support for managing a "primary" set of selected items, |
| * in addition to a "provisional" set of selected items using conventional |
| * {@link java.util.Collections}-like methods. |
| * |
| * <p> |
| * Create an instance of SelectionTracker using {@link Builder SelectionTracker.Builder}. |
| * |
| * <p> |
| * <b>Inspecting the current selection</b> |
| * |
| * <p> |
| * The underlying selection is described by the {@link Selection} class. |
| * |
| * <p> |
| * A live view of the current selection can be obtained using {@link #getSelection}. Changes made |
| * to the selection using SelectionTracker will be immediately reflected in this instance. |
| * |
| * <p> |
| * To obtain a stable snapshot of the selection use {@link #copySelection(MutableSelection)}. |
| * |
| * <p> |
| * Selection state for an individual item can be obtained using {@link #isSelected(Object)}. |
| * |
| * <p> |
| * <b>Provisional Selection</b> |
| * |
| * <p> |
| * Provisional selection exists to address issues where a transitory selection might |
| * momentarily intersect with a previously established selection resulting in a some |
| * or all of the established selection being erased. Such situations may arise |
| * when band selection is being performed in "additive" mode (e.g. SHIFT or CTRL is pressed |
| * on the keyboard prior to mouse down), or when there's an active gesture selection |
| * (which can be initiated by long pressing an unselected item while there is an |
| * existing selection). |
| * |
| * <p> |
| * A provisional selection can be abandoned, or merged into the primary selection. |
| * |
| * <p> |
| * <b>Enforcing selection policies</b> |
| * |
| * <p> |
| * Which items can be selected by the user is a matter of policy in an Application. |
| * Developers supply these policies by way of {@link SelectionPredicate}. |
| * |
| * @param <K> Selection key type. @see {@link StorageStrategy} for supported types. |
| */ |
| public abstract class SelectionTracker<K> { |
| |
| /** |
| * This value is included in the payload when SelectionTracker notifies RecyclerView |
| * of changes to selection. Look for this value in the {@code payload} |
| * Object argument supplied to |
| * {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder |
| * Adapter#onBindViewHolder}. |
| * If present the call is occurring in response to a selection state change. |
| * This would be a good opportunity to animate changes between unselected and selected state. |
| * When state is being restored, this argument will not be present. |
| */ |
| public static final String SELECTION_CHANGED_MARKER = "Selection-Changed"; |
| |
| /** |
| * Adds {@code observer} to be notified when changes to selection occur. |
| * |
| * <p> |
| * Use an observer to track attributes about the selection and |
| * update the UI to reflect the state of the selection. For example, an author |
| * may use an observer to control the enabled status of menu items, |
| * or to initiate {@link android.view.ActionMode}. |
| */ |
| public abstract void addObserver(SelectionObserver observer); |
| |
| /** @return true if has a selection */ |
| public abstract boolean hasSelection(); |
| |
| /** |
| * Returns a Selection object that provides a live view on the current selection. |
| * |
| * @return The current selection. |
| * @see #copySelection(MutableSelection) on how to get a snapshot |
| * of the selection that will not reflect future changes |
| * to selection. |
| */ |
| public abstract Selection getSelection(); |
| |
| /** |
| * Updates {@code dest} to reflect the current selection. |
| */ |
| public abstract void copySelection(@NonNull MutableSelection<K> dest); |
| |
| /** |
| * @return true if the item specified by its id is selected. Shorthand for |
| * {@code getSelection().contains(K)}. |
| */ |
| public abstract boolean isSelected(@Nullable K key); |
| |
| /** |
| * Restores the selected state of specified items. Used in cases such as restore the selection |
| * after rotation etc. Provisional selection is not restored. |
| * |
| * <p> |
| * This affords clients the ability to restore selection from selection saved |
| * in Activity state. |
| * |
| * @see StorageStrategy details on selection state support. |
| * |
| * @param selection selection being restored. |
| */ |
| protected abstract void restoreSelection(@NonNull Selection<K> selection); |
| |
| /** |
| * Clears both primary and provisional selections. |
| * |
| * @return true if primary selection changed. |
| */ |
| public abstract boolean clearSelection(); |
| |
| /** |
| * Sets the selected state of the specified items if permitted after consulting |
| * SelectionPredicate. |
| */ |
| public abstract boolean setItemsSelected(@NonNull Iterable<K> keys, boolean selected); |
| |
| /** |
| * Attempts to select an item. |
| * |
| * @return true if the item was selected. False if the item could not be selected, or was |
| * was already selected. |
| */ |
| public abstract boolean select(@NonNull K key); |
| |
| /** |
| * Attempts to deselect an item. |
| * |
| * @return true if the item was deselected. False if the item could not be deselected, or was |
| * was already un-selected. |
| */ |
| public abstract boolean deselect(@NonNull K key); |
| |
| abstract void onDataSetChanged(); |
| |
| /** |
| * Attempts to establish a range selection at {@code position}, selecting the item |
| * at {@code position} if needed. |
| * |
| * @param position The "anchor" position for the range. Subsequent range operations |
| * (primarily keyboard and mouse based operations like SHIFT + click) |
| * work with the established anchor point to define selection ranges. |
| */ |
| abstract void startRange(int position); |
| |
| /** |
| * Sets the end point for the active range selection. |
| * |
| * <p> |
| * This function should only be called when a range selection is active |
| * (see {@link #isRangeActive()}. Items in the range [anchor, end] will be |
| * selected after consulting SelectionPredicate. |
| * |
| * @param position The new end position for the selection range. |
| * @throws IllegalStateException if a range selection is not active. Range selection |
| * must have been started by a call to {@link #startRange(int)}. |
| */ |
| abstract void extendRange(int position); |
| |
| /** |
| * Clears an in-progress range selection. Provisional range selection established |
| * using {@link #extendProvisionalRange(int)} will be cleared (unless |
| * {@link #mergeProvisionalSelection()} is called first.) |
| */ |
| abstract void endRange(); |
| |
| /** |
| * @return Whether or not there is a current range selection active. |
| */ |
| abstract boolean isRangeActive(); |
| |
| /** |
| * Establishes the "anchor" at which a selection range begins. This "anchor" is consulted |
| * when determining how to extend, and modify selection ranges. Calling this when a |
| * range selection is active will reset the range selection. |
| * |
| * TODO: Reconcile this with startRange. Maybe just docs need to be updated. |
| * |
| * @param position the anchor position. Must already be selected. |
| */ |
| abstract void anchorRange(int position); |
| |
| /** |
| * Creates a provisional selection from anchor to {@code position}. |
| * |
| * @param position the end point. |
| */ |
| abstract void extendProvisionalRange(int position); |
| |
| /** |
| * Sets the provisional selection, replacing any existing selection. |
| * @param newSelection |
| */ |
| abstract void setProvisionalSelection(@NonNull Set<K> newSelection); |
| |
| /** |
| * Clears any existing provisional selection |
| */ |
| abstract void clearProvisionalSelection(); |
| |
| /** |
| * Converts the provisional selection into primary selection, then clears |
| * provisional selection. |
| */ |
| abstract void mergeProvisionalSelection(); |
| |
| /** |
| * Preserves selection, if any. Call this method from Activity#onSaveInstanceState |
| * |
| * @param state Bundle instance supplied to onSaveInstanceState. |
| */ |
| public abstract void onSaveInstanceState(@NonNull Bundle state); |
| |
| /** |
| * Restores selection from previously saved state. Call this method from |
| * Activity#onCreate. |
| * |
| * @param state Bundle instance supplied to onCreate. |
| */ |
| public abstract void onRestoreInstanceState(@Nullable Bundle state); |
| |
| /** |
| * Observer class providing access to information about Selection state changes. |
| * |
| * @param <K> Selection key type. @see {@link StorageStrategy} for supported types. |
| */ |
| public abstract static class SelectionObserver<K> { |
| |
| /** |
| * Called when the state of an item has been changed. |
| */ |
| public void onItemStateChanged(@NonNull K key, boolean selected) { |
| } |
| |
| /** |
| * Called when the underlying data set has changed. After this method is called |
| * SelectionTracker will traverse the existing selection, |
| * calling {@link #onItemStateChanged(K, boolean)} for each selected item, |
| * and deselecting any items that cannot be selected given the updated data-set |
| * (and after consulting SelectionPredicate). |
| */ |
| public void onSelectionRefresh() { |
| } |
| |
| /** |
| * Called immediately after completion of any set of changes, excluding |
| * those resulting in calls to {@link #onSelectionRefresh()} and |
| * {@link #onSelectionRestored()}. |
| */ |
| public void onSelectionChanged() { |
| } |
| |
| /** |
| * Called immediately after selection is restored. |
| * {@link #onItemStateChanged(K, boolean)} will *not* be called |
| * for individual items in the selection. |
| */ |
| public void onSelectionRestored() { |
| } |
| } |
| |
| /** |
| * Implement SelectionPredicate to control when items can be selected or unselected. |
| * See {@link Builder#withSelectionPredicate(SelectionPredicate)}. |
| * |
| * @param <K> Selection key type. @see {@link StorageStrategy} for supported types. |
| */ |
| public abstract static class SelectionPredicate<K> { |
| |
| /** |
| * Validates a change to selection for a specific key. |
| * |
| * @param key the item key |
| * @param nextState the next potential selected/unselected state |
| * @return true if the item at {@code id} can be set to {@code nextState}. |
| */ |
| public abstract boolean canSetStateForKey(@NonNull K key, boolean nextState); |
| |
| /** |
| * Validates a change to selection for a specific position. If necessary |
| * use {@link ItemKeyProvider} to identy associated key. |
| * |
| * @param position the item position |
| * @param nextState the next potential selected/unselected state |
| * @return true if the item at {@code id} can be set to {@code nextState}. |
| */ |
| public abstract boolean canSetStateAtPosition(int position, boolean nextState); |
| |
| /** |
| * Permits restriction to single selection mode. Single selection mode has |
| * unique behaviors in that it'll deselect an item already selected |
| * in order to select the new item. |
| * |
| * <p> |
| * In order to limit the number of items that can be selected, |
| * use {@link #canSetStateForKey(Object, boolean)} and |
| * {@link #canSetStateAtPosition(int, boolean)}. |
| * |
| * @return true if more than a single item can be selected. |
| */ |
| public abstract boolean canSelectMultiple(); |
| } |
| |
| /** |
| * Builder is the primary mechanism for create a {@link SelectionTracker} that |
| * can be used with your RecyclerView. Once installed, users will be able to create and |
| * manipulate selection using a variety of intuitive techniques like tap, gesture, |
| * and mouse lasso. |
| * |
| * <p> |
| * Example usage: |
| * <pre>SelectionTracker<Uri> tracker = new SelectionTracker.Builder<>( |
| * "my-uri-selection", |
| * recyclerView, |
| * new DemoStableIdProvider(recyclerView.getAdapter()), |
| * new MyDetailsLookup(recyclerView), |
| * StorageStrategy.createParcelableStorage(Uri.class)) |
| * .build(); |
| *</pre> |
| * |
| * <p> |
| * <b>Restricting which items can be selected and limiting selection size</b> |
| * |
| * <p> |
| * {@link SelectionPredicate} provides a mechanism to restrict which Items can be selected, |
| * to limit the number of items that can be selected, as well as allowing the selection |
| * code to be placed into "single select" mode, which as the name indicates, constrains |
| * the selection size to a single item. |
| * |
| * <p>Configuring the tracker for single single selection support can be done |
| * by supplying {@link SelectionPredicates#createSelectSingleAnything()}. |
| * |
| * SelectionTracker<String> tracker = new SelectionTracker.Builder<>( |
| * "my-string-selection", |
| * recyclerView, |
| * new DemoStableIdProvider(recyclerView.getAdapter()), |
| * new MyDetailsLookup(recyclerView), |
| * StorageStrategy.createStringStorage()) |
| * .withSelectionPredicate(SelectionPredicates#createSelectSingleAnything()) |
| * .build(); |
| *</pre> |
| * <p> |
| * <b>Retaining state across Android lifecycle events</b> |
| * |
| * <p> |
| * Support for storage/persistence of selection must be configured and invoked manually |
| * owing to its reliance on Activity lifecycle events. |
| * Failure to include support for selection storage will result in the active selection |
| * being lost when the Activity receives a configuration change (e.g. rotation) |
| * or when the application process is destroyed by the OS to reclaim resources. |
| * |
| * <p> |
| * <b>Key Type</b> |
| * |
| * <p> |
| * Developers must decide on the key type used to identify selected items. Support |
| * is provided for three types: {@link Parcelable}, {@link String}, and {@link Long}. |
| * |
| * <p> |
| * {@link Parcelable}: Any Parcelable type can be used as the selection key. This is especially |
| * useful in conjunction with {@link android.net.Uri} as the Android URI implementation is both |
| * parcelable and makes for a natural stable selection key for values represented by |
| * the Android Content Provider framework. If items in your view are associated with |
| * stable {@code content://} uris, you should use Uri for your key type. |
| * |
| * <p> |
| * {@link String}: Use String when a string based stable identifier is available. |
| * |
| * <p> |
| * {@link Long}: Use Long when RecyclerView's long stable ids are |
| * already in use. It comes with some limitations, however, as access to stable ids |
| * at runtime is limited. Band selection support is not available when using the default |
| * long key storage implementation. See {@link StableIdKeyProvider} for details. |
| * |
| * <p> |
| * Usage: |
| * |
| * <pre> |
| * private SelectionTracker<Uri> mTracker; |
| * |
| * public void onCreate(Bundle savedInstanceState) { |
| * // See above for details on constructing a SelectionTracker instance. |
| * |
| * if (savedInstanceState != null) { |
| * mTracker.onRestoreInstanceState(savedInstanceState); |
| * } |
| * } |
| * |
| * protected void onSaveInstanceState(Bundle outState) { |
| * super.onSaveInstanceState(outState); |
| * mTracker.onSaveInstanceState(outState); |
| * } |
| * </pre> |
| * |
| * @param <K> Selection key type. Built in support is provided for {@link String}, |
| * {@link Long}, and {@link Parcelable}. {@link StorageStrategy} |
| * provides factory methods for each type: |
| * {@link StorageStrategy#createStringStorage()}, |
| * {@link StorageStrategy#createParcelableStorage(Class)}, |
| * {@link StorageStrategy#createLongStorage()} |
| */ |
| public static final class Builder<K> { |
| |
| private final RecyclerView mRecyclerView; |
| private final RecyclerView.Adapter<?> mAdapter; |
| private final Context mContext; |
| private final String mSelectionId; |
| private final StorageStrategy<K> mStorage; |
| |
| private SelectionPredicate<K> mSelectionPredicate = |
| SelectionPredicates.createSelectAnything(); |
| private OperationMonitor mMonitor = new OperationMonitor(); |
| private ItemKeyProvider<K> mKeyProvider; |
| private ItemDetailsLookup<K> mDetailsLookup; |
| |
| private FocusDelegate<K> mFocusDelegate = FocusDelegate.dummy(); |
| |
| private OnItemActivatedListener<K> mOnItemActivatedListener; |
| private OnDragInitiatedListener mOnDragInitiatedListener; |
| private OnContextClickListener mOnContextClickListener; |
| |
| private BandPredicate mBandPredicate; |
| private int mBandOverlayId = R.drawable.selection_band_overlay; |
| |
| private int[] mGestureToolTypes = new int[] { |
| MotionEvent.TOOL_TYPE_FINGER, |
| MotionEvent.TOOL_TYPE_UNKNOWN |
| }; |
| |
| private int[] mBandToolTypes = new int[] { |
| MotionEvent.TOOL_TYPE_MOUSE |
| }; |
| |
| /** |
| * Creates a new SelectionTracker.Builder useful for configuring and creating |
| * a new SelectionTracker for use with your {@link RecyclerView}. |
| * |
| * @param selectionId A unique string identifying this selection in the context |
| * of the activity or fragment. |
| * @param recyclerView the owning RecyclerView |
| * @param keyProvider the source of selection keys |
| * @param detailsLookup the source of information about RecyclerView items. |
| * @param storage Strategy for type-safe storage of selection state in |
| * {@link Bundle}. |
| */ |
| public Builder( |
| @NonNull String selectionId, |
| @NonNull RecyclerView recyclerView, |
| @NonNull ItemKeyProvider<K> keyProvider, |
| @NonNull ItemDetailsLookup<K> detailsLookup, |
| @NonNull StorageStrategy<K> storage) { |
| |
| checkArgument(selectionId != null); |
| checkArgument(!selectionId.trim().isEmpty()); |
| checkArgument(recyclerView != null); |
| |
| mSelectionId = selectionId; |
| mRecyclerView = recyclerView; |
| mContext = recyclerView.getContext(); |
| mAdapter = recyclerView.getAdapter(); |
| |
| checkArgument(mAdapter != null); |
| checkArgument(keyProvider != null); |
| checkArgument(detailsLookup != null); |
| checkArgument(storage != null); |
| |
| mDetailsLookup = detailsLookup; |
| mKeyProvider = keyProvider; |
| mStorage = storage; |
| |
| mBandPredicate = new BandPredicate.NonDraggableArea(mRecyclerView, detailsLookup); |
| } |
| |
| /** |
| * Install selection predicate. |
| * |
| * @param predicate the predicate to be used. |
| * @return this |
| */ |
| public Builder<K> withSelectionPredicate( |
| @NonNull SelectionPredicate<K> predicate) { |
| |
| checkArgument(predicate != null); |
| mSelectionPredicate = predicate; |
| return this; |
| } |
| |
| /** |
| * Add operation monitor allowing access to information about active |
| * operations (like band selection and gesture selection). |
| * |
| * @param monitor the monitor to be used |
| * @return this |
| */ |
| public Builder<K> withOperationMonitor( |
| @NonNull OperationMonitor monitor) { |
| |
| checkArgument(monitor != null); |
| mMonitor = monitor; |
| return this; |
| } |
| |
| /** |
| * Add focus delegate to interact with selection related focus changes. |
| * |
| * @param delegate the delegate to be used |
| * @return this |
| */ |
| public Builder<K> withFocusDelegate(@NonNull FocusDelegate<K> delegate) { |
| checkArgument(delegate != null); |
| mFocusDelegate = delegate; |
| return this; |
| } |
| |
| /** |
| * Adds an item activation listener. Respond to taps/enter/double-click on items. |
| * |
| * @param listener the listener to be used |
| * @return this |
| */ |
| public Builder<K> withOnItemActivatedListener( |
| @NonNull OnItemActivatedListener<K> listener) { |
| |
| checkArgument(listener != null); |
| |
| mOnItemActivatedListener = listener; |
| return this; |
| } |
| |
| /** |
| * Adds a context click listener. Respond to right-click. |
| * |
| * @param listener the listener to be used |
| * @return this |
| */ |
| public Builder<K> withOnContextClickListener( |
| @NonNull OnContextClickListener listener) { |
| |
| checkArgument(listener != null); |
| |
| mOnContextClickListener = listener; |
| return this; |
| } |
| |
| /** |
| * Adds a drag initiated listener. Add support for drag and drop. |
| * |
| * @param listener the listener to be used |
| * @return this |
| */ |
| public Builder<K> withOnDragInitiatedListener( |
| @NonNull OnDragInitiatedListener listener) { |
| |
| checkArgument(listener != null); |
| |
| mOnDragInitiatedListener = listener; |
| return this; |
| } |
| |
| /** |
| * Replaces default tap and gesture tool-types. Defaults are: |
| * {@link MotionEvent#TOOL_TYPE_FINGER} and {@link MotionEvent#TOOL_TYPE_UNKNOWN}. |
| * |
| * @param toolTypes the tool types to be used |
| * @return this |
| */ |
| public Builder<K> withGestureTooltypes(int... toolTypes) { |
| mGestureToolTypes = toolTypes; |
| return this; |
| } |
| |
| /** |
| * Replaces default band overlay. |
| * |
| * @param bandOverlayId |
| * @return this |
| */ |
| public Builder<K> withBandOverlay(@DrawableRes int bandOverlayId) { |
| mBandOverlayId = bandOverlayId; |
| return this; |
| } |
| |
| /** |
| * Replaces default band predicate. |
| * @param bandPredicate |
| * @return this |
| */ |
| public Builder<K> withBandPredicate(@NonNull BandPredicate bandPredicate) { |
| checkArgument(bandPredicate != null); |
| |
| mBandPredicate = bandPredicate; |
| return this; |
| } |
| |
| /** |
| * Replaces default band selection tool-types. Defaults are: |
| * {@link MotionEvent#TOOL_TYPE_MOUSE}. |
| * |
| * @param toolTypes the tool types to be used |
| * @return this |
| */ |
| public Builder<K> withBandTooltypes(int... toolTypes) { |
| mBandToolTypes = toolTypes; |
| return this; |
| } |
| |
| /** |
| * Prepares and returns a SelectionTracker. |
| * |
| * @return this |
| */ |
| public SelectionTracker<K> build() { |
| |
| SelectionTracker<K> tracker = new DefaultSelectionTracker<>( |
| mSelectionId, mKeyProvider, mSelectionPredicate, mStorage); |
| |
| // Event glue between RecyclerView and SelectionTracker keeps the classes separate |
| // so that a SelectionTracker can be shared across RecyclerView instances that |
| // represent the same data in different ways. |
| EventBridge.install(mAdapter, tracker, mKeyProvider); |
| |
| AutoScroller scroller = |
| new ViewAutoScroller(ViewAutoScroller.createScrollHost(mRecyclerView)); |
| |
| // Setup basic input handling, with the touch handler as the default consumer |
| // of events. If mouse handling is configured as well, the mouse input |
| // related handlers will intercept mouse input events. |
| |
| // GestureRouter is responsible for routing GestureDetector events |
| // to tool-type specific handlers. |
| GestureRouter<MotionInputHandler> gestureRouter = new GestureRouter<>(); |
| GestureDetector gestureDetector = new GestureDetector(mContext, gestureRouter); |
| |
| // TouchEventRouter takes its name from RecyclerView#OnItemTouchListener. |
| // Despite "Touch" being in the name, it receives events for all types of tools. |
| // This class is responsible for routing events to tool-type specific handlers, |
| // and if not handled by a handler, on to a GestureDetector for analysis. |
| TouchEventRouter eventRouter = new TouchEventRouter(gestureDetector); |
| |
| // GestureSelectionHelper provides logic that interprets a combination |
| // of motions and gestures in order to provide gesture driven selection support |
| // when used in conjunction with RecyclerView. |
| final GestureSelectionHelper gestureHelper = |
| GestureSelectionHelper.create(tracker, mRecyclerView, scroller, mMonitor); |
| |
| // Finally hook the framework up to listening to recycle view events. |
| mRecyclerView.addOnItemTouchListener(eventRouter); |
| |
| // But before you move on, there's more work to do. Event plumbing has been |
| // installed, but we haven't registered any of our helpers or callbacks. |
| // Helpers contain predefined logic converting events into selection related events. |
| // Callbacks provide developers the ability to reponspond to other types of |
| // events (like "activate" a tapped item). This is broken up into two main |
| // suites, one for "touch" and one for "mouse", though both can and should (usually) |
| // be configured to handle other types of input (to satisfy user expectation).); |
| |
| // Internally, the code doesn't permit nullable listeners, so we lazily |
| // initialize dummy instances if the developer didn't supply a real listener. |
| mOnDragInitiatedListener = (mOnDragInitiatedListener != null) |
| ? mOnDragInitiatedListener |
| : new OnDragInitiatedListener() { |
| @Override |
| public boolean onDragInitiated(@NonNull MotionEvent e) { |
| return false; |
| } |
| }; |
| |
| mOnItemActivatedListener = (mOnItemActivatedListener != null) |
| ? mOnItemActivatedListener |
| : new OnItemActivatedListener<K>() { |
| @Override |
| public boolean onItemActivated( |
| @NonNull ItemDetailsLookup.ItemDetails<K> item, |
| @NonNull MotionEvent e) { |
| return false; |
| } |
| }; |
| |
| mOnContextClickListener = (mOnContextClickListener != null) |
| ? mOnContextClickListener |
| : new OnContextClickListener() { |
| @Override |
| public boolean onContextClick(@NonNull MotionEvent e) { |
| return false; |
| } |
| }; |
| |
| // Provides high level glue for binding touch events |
| // and gestures to selection framework. |
| TouchInputHandler<K> touchHandler = new TouchInputHandler<K>( |
| tracker, |
| mKeyProvider, |
| mDetailsLookup, |
| mSelectionPredicate, |
| new Runnable() { |
| @Override |
| public void run() { |
| if (mSelectionPredicate.canSelectMultiple()) { |
| gestureHelper.start(); |
| } |
| } |
| }, |
| mOnDragInitiatedListener, |
| mOnItemActivatedListener, |
| mFocusDelegate, |
| new Runnable() { |
| @Override |
| public void run() { |
| mRecyclerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); |
| } |
| }); |
| |
| for (int toolType : mGestureToolTypes) { |
| gestureRouter.register(toolType, touchHandler); |
| eventRouter.register(toolType, gestureHelper); |
| } |
| |
| // Provides high level glue for binding mouse events and gestures |
| // to selection framework. |
| MouseInputHandler<K> mouseHandler = new MouseInputHandler<>( |
| tracker, |
| mKeyProvider, |
| mDetailsLookup, |
| mOnContextClickListener, |
| mOnItemActivatedListener, |
| mFocusDelegate); |
| |
| for (int toolType : mBandToolTypes) { |
| gestureRouter.register(toolType, mouseHandler); |
| } |
| |
| // Band selection not supported in single select mode, or when key access |
| // is limited to anything less than the entire corpus. |
| if (mKeyProvider.hasAccess(ItemKeyProvider.SCOPE_MAPPED) |
| && mSelectionPredicate.canSelectMultiple()) { |
| // BandSelectionHelper provides support for band selection on-top of a RecyclerView |
| // instance. Given the recycling nature of RecyclerView BandSelectionController |
| // necessarily models and caches list/grid information as the user's pointer |
| // interacts with the item in the RecyclerView. Selectable items that intersect |
| // with the band, both on and off screen, are selected. |
| BandSelectionHelper bandHelper = BandSelectionHelper.create( |
| mRecyclerView, |
| scroller, |
| mBandOverlayId, |
| mKeyProvider, |
| tracker, |
| mSelectionPredicate, |
| mBandPredicate, |
| mFocusDelegate, |
| mMonitor); |
| |
| for (int toolType : mBandToolTypes) { |
| eventRouter.register(toolType, bandHelper); |
| } |
| } |
| |
| return tracker; |
| } |
| } |
| } |