Home | History | Annotate | Download | only in selection
      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 
     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 
     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 
     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 
     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      */
     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 
    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      */
    135     private void updateRange(int begin, int end, boolean selected, @RangeType int type) {
    136         mUpdater.updateForRange(begin, end, selected, type);
    137     }
    138 
    139     @Override
    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 {
    150         void updateForRange(int begin, int end, boolean selected, @RangeType int type);
    151     }
    152 }
    153