Home | History | Annotate | Download | only in selection
      1 /*
      2  * Copyright 2018 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 androidx.recyclerview.selection;
     18 
     19 import static androidx.core.util.Preconditions.checkArgument;
     20 
     21 import android.os.Bundle;
     22 import android.os.Parcelable;
     23 
     24 import androidx.annotation.NonNull;
     25 import androidx.annotation.Nullable;
     26 import androidx.annotation.VisibleForTesting;
     27 
     28 import java.util.ArrayList;
     29 
     30 /**
     31  * Strategy for storing keys in saved state. Extend this class when using custom
     32  * key types that aren't supported by default. Prefer use of builtin storage strategies:
     33  * {@link #createStringStorage()}, {@link #createLongStorage()},
     34  * {@link #createParcelableStorage(Class)}.
     35  *
     36  * <p>
     37  * See
     38  * {@link androidx.recyclerview.selection.SelectionTracker.Builder SelectionTracker.Builder}
     39  * for more detailed advice on which key type to use for your selection keys.
     40  *
     41  * @param <K> Selection key type. Built in support is provided for String, Long, and Parcelable
     42  *           types. Use the respective factory method to create a StorageStrategy instance
     43  *           appropriate to the desired type.
     44  *           {@link #createStringStorage()},
     45  *           {@link #createParcelableStorage(Class)},
     46  *           {@link #createLongStorage()}
     47  */
     48 public abstract class StorageStrategy<K> {
     49 
     50     @VisibleForTesting
     51     static final String SELECTION_ENTRIES = "androidx.recyclerview.selection.entries";
     52 
     53     @VisibleForTesting
     54     static final String SELECTION_KEY_TYPE = "androidx.recyclerview.selection.type";
     55 
     56     private final Class<K> mType;
     57 
     58     /**
     59      * Creates a new instance.
     60      *
     61      * @param type the key type class that is being used.
     62      */
     63     public StorageStrategy(@NonNull Class<K> type) {
     64         checkArgument(type != null);
     65         mType = type;
     66     }
     67 
     68     /**
     69      * Create a {@link Selection} from supplied {@link Bundle}.
     70      *
     71      * @param state Bundle instance that may contain parceled Selection instance.
     72      * @return
     73      */
     74     public abstract @Nullable Selection<K> asSelection(@NonNull Bundle state);
     75 
     76     /**
     77      * Creates a {@link Bundle} from supplied {@link Selection}.
     78      *
     79      * @param selection The selection to asBundle.
     80      * @return
     81      */
     82     public abstract @NonNull Bundle asBundle(@NonNull Selection<K> selection);
     83 
     84     String getKeyTypeName() {
     85         return mType.getCanonicalName();
     86     }
     87 
     88     /**
     89      * @return StorageStrategy suitable for use with {@link Parcelable} keys
     90      * (like {@link android.net.Uri}).
     91      */
     92     public static <K extends Parcelable> StorageStrategy<K> createParcelableStorage(Class<K> type) {
     93         return new ParcelableStorageStrategy(type);
     94     }
     95 
     96     /**
     97      * @return StorageStrategy suitable for use with {@link String} keys.
     98      */
     99     public static StorageStrategy<String> createStringStorage() {
    100         return new StringStorageStrategy();
    101     }
    102 
    103     /**
    104      * @return StorageStrategy suitable for use with {@link Long} keys.
    105      */
    106     public static StorageStrategy<Long> createLongStorage() {
    107         return new LongStorageStrategy();
    108     }
    109 
    110     private static class StringStorageStrategy extends StorageStrategy<String> {
    111 
    112         StringStorageStrategy() {
    113             super(String.class);
    114         }
    115 
    116         @Override
    117         public @Nullable Selection<String> asSelection(@NonNull Bundle state) {
    118 
    119             String keyType = state.getString(SELECTION_KEY_TYPE, null);
    120             if (keyType == null || !keyType.equals(getKeyTypeName())) {
    121                 return null;
    122             }
    123 
    124             @Nullable ArrayList<String> stored = state.getStringArrayList(SELECTION_ENTRIES);
    125             if (stored == null) {
    126                 return null;
    127             }
    128 
    129             Selection<String> selection = new Selection<>();
    130             selection.mSelection.addAll(stored);
    131             return selection;
    132         }
    133 
    134         @Override
    135         public @NonNull Bundle asBundle(@NonNull Selection<String> selection) {
    136 
    137             Bundle bundle = new Bundle();
    138 
    139             bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName());
    140 
    141             ArrayList<String> value = new ArrayList<>(selection.size());
    142             value.addAll(selection.mSelection);
    143             bundle.putStringArrayList(SELECTION_ENTRIES, value);
    144 
    145             return bundle;
    146         }
    147     }
    148 
    149     private static class LongStorageStrategy extends StorageStrategy<Long> {
    150 
    151         LongStorageStrategy() {
    152             super(Long.class);
    153         }
    154 
    155         @Override
    156         public @Nullable Selection<Long> asSelection(@NonNull Bundle state) {
    157             String keyType = state.getString(SELECTION_KEY_TYPE, null);
    158             if (keyType == null || !keyType.equals(getKeyTypeName())) {
    159                 return null;
    160             }
    161 
    162             @Nullable long[] stored = state.getLongArray(SELECTION_ENTRIES);
    163             if (stored == null) {
    164                 return null;
    165             }
    166 
    167             Selection<Long> selection = new Selection<>();
    168             for (long key : stored) {
    169                 selection.mSelection.add(key);
    170             }
    171             return selection;
    172         }
    173 
    174         @Override
    175         public @NonNull Bundle asBundle(@NonNull Selection<Long> selection) {
    176 
    177             Bundle bundle = new Bundle();
    178             bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName());
    179 
    180             long[] value = new long[selection.size()];
    181             int i = 0;
    182             for (Long key : selection) {
    183                 value[i++] = key;
    184             }
    185             bundle.putLongArray(SELECTION_ENTRIES, value);
    186 
    187             return bundle;
    188         }
    189     }
    190 
    191     private static class ParcelableStorageStrategy<K extends Parcelable>
    192             extends StorageStrategy<K> {
    193 
    194         ParcelableStorageStrategy(Class<K> type) {
    195             super(type);
    196             checkArgument(Parcelable.class.isAssignableFrom(type));
    197         }
    198 
    199         @Override
    200         public @Nullable Selection<K> asSelection(@NonNull Bundle state) {
    201 
    202             String keyType = state.getString(SELECTION_KEY_TYPE, null);
    203             if (keyType == null || !keyType.equals(getKeyTypeName())) {
    204                 return null;
    205             }
    206 
    207             @Nullable ArrayList<K> stored = state.getParcelableArrayList(SELECTION_ENTRIES);
    208             if (stored == null) {
    209                 return null;
    210             }
    211 
    212             Selection<K> selection = new Selection<>();
    213             selection.mSelection.addAll(stored);
    214             return selection;
    215         }
    216 
    217         @Override
    218         public @NonNull Bundle asBundle(@NonNull Selection<K> selection) {
    219 
    220             Bundle bundle = new Bundle();
    221             bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName());
    222 
    223             ArrayList<K> value = new ArrayList<>(selection.size());
    224             value.addAll(selection.mSelection);
    225             bundle.putParcelableArrayList(SELECTION_ENTRIES, value);
    226 
    227             return bundle;
    228         }
    229     }
    230 }
    231