| /* |
| * Copyright 2018 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.recyclerview.selection; |
| |
| import static androidx.core.util.Preconditions.checkArgument; |
| |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Strategy for storing keys in saved state. Extend this class when using custom |
| * key types that aren't supported by default. Prefer use of builtin storage strategies: |
| * {@link #createStringStorage()}, {@link #createLongStorage()}, |
| * {@link #createParcelableStorage(Class)}. |
| * |
| * <p> |
| * See |
| * {@link androidx.recyclerview.selection.SelectionTracker.Builder SelectionTracker.Builder} |
| * for more detailed advice on which key type to use for your selection keys. |
| * |
| * @param <K> Selection key type. Built in support is provided for String, Long, and Parcelable |
| * types. Use the respective factory method to create a StorageStrategy instance |
| * appropriate to the desired type. |
| * {@link #createStringStorage()}, |
| * {@link #createParcelableStorage(Class)}, |
| * {@link #createLongStorage()} |
| */ |
| public abstract class StorageStrategy<K> { |
| |
| @VisibleForTesting |
| static final String SELECTION_ENTRIES = "androidx.recyclerview.selection.entries"; |
| |
| @VisibleForTesting |
| static final String SELECTION_KEY_TYPE = "androidx.recyclerview.selection.type"; |
| |
| private final Class<K> mType; |
| |
| /** |
| * Creates a new instance. |
| * |
| * @param type the key type class that is being used. |
| */ |
| public StorageStrategy(@NonNull Class<K> type) { |
| checkArgument(type != null); |
| mType = type; |
| } |
| |
| /** |
| * Create a {@link Selection} from supplied {@link Bundle}. |
| * |
| * @param state Bundle instance that may contain parceled Selection instance. |
| * @return |
| */ |
| public abstract @Nullable Selection<K> asSelection(@NonNull Bundle state); |
| |
| /** |
| * Creates a {@link Bundle} from supplied {@link Selection}. |
| * |
| * @param selection The selection to asBundle. |
| * @return |
| */ |
| public abstract @NonNull Bundle asBundle(@NonNull Selection<K> selection); |
| |
| String getKeyTypeName() { |
| return mType.getCanonicalName(); |
| } |
| |
| /** |
| * @return StorageStrategy suitable for use with {@link Parcelable} keys |
| * (like {@link android.net.Uri}). |
| */ |
| public static <K extends Parcelable> StorageStrategy<K> createParcelableStorage(Class<K> type) { |
| return new ParcelableStorageStrategy(type); |
| } |
| |
| /** |
| * @return StorageStrategy suitable for use with {@link String} keys. |
| */ |
| public static StorageStrategy<String> createStringStorage() { |
| return new StringStorageStrategy(); |
| } |
| |
| /** |
| * @return StorageStrategy suitable for use with {@link Long} keys. |
| */ |
| public static StorageStrategy<Long> createLongStorage() { |
| return new LongStorageStrategy(); |
| } |
| |
| private static class StringStorageStrategy extends StorageStrategy<String> { |
| |
| StringStorageStrategy() { |
| super(String.class); |
| } |
| |
| @Override |
| public @Nullable Selection<String> asSelection(@NonNull Bundle state) { |
| |
| String keyType = state.getString(SELECTION_KEY_TYPE, null); |
| if (keyType == null || !keyType.equals(getKeyTypeName())) { |
| return null; |
| } |
| |
| @Nullable ArrayList<String> stored = state.getStringArrayList(SELECTION_ENTRIES); |
| if (stored == null) { |
| return null; |
| } |
| |
| Selection<String> selection = new Selection<>(); |
| selection.mSelection.addAll(stored); |
| return selection; |
| } |
| |
| @Override |
| public @NonNull Bundle asBundle(@NonNull Selection<String> selection) { |
| |
| Bundle bundle = new Bundle(); |
| |
| bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName()); |
| |
| ArrayList<String> value = new ArrayList<>(selection.size()); |
| value.addAll(selection.mSelection); |
| bundle.putStringArrayList(SELECTION_ENTRIES, value); |
| |
| return bundle; |
| } |
| } |
| |
| private static class LongStorageStrategy extends StorageStrategy<Long> { |
| |
| LongStorageStrategy() { |
| super(Long.class); |
| } |
| |
| @Override |
| public @Nullable Selection<Long> asSelection(@NonNull Bundle state) { |
| String keyType = state.getString(SELECTION_KEY_TYPE, null); |
| if (keyType == null || !keyType.equals(getKeyTypeName())) { |
| return null; |
| } |
| |
| @Nullable long[] stored = state.getLongArray(SELECTION_ENTRIES); |
| if (stored == null) { |
| return null; |
| } |
| |
| Selection<Long> selection = new Selection<>(); |
| for (long key : stored) { |
| selection.mSelection.add(key); |
| } |
| return selection; |
| } |
| |
| @Override |
| public @NonNull Bundle asBundle(@NonNull Selection<Long> selection) { |
| |
| Bundle bundle = new Bundle(); |
| bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName()); |
| |
| long[] value = new long[selection.size()]; |
| int i = 0; |
| for (Long key : selection) { |
| value[i++] = key; |
| } |
| bundle.putLongArray(SELECTION_ENTRIES, value); |
| |
| return bundle; |
| } |
| } |
| |
| private static class ParcelableStorageStrategy<K extends Parcelable> |
| extends StorageStrategy<K> { |
| |
| ParcelableStorageStrategy(Class<K> type) { |
| super(type); |
| checkArgument(Parcelable.class.isAssignableFrom(type)); |
| } |
| |
| @Override |
| public @Nullable Selection<K> asSelection(@NonNull Bundle state) { |
| |
| String keyType = state.getString(SELECTION_KEY_TYPE, null); |
| if (keyType == null || !keyType.equals(getKeyTypeName())) { |
| return null; |
| } |
| |
| @Nullable ArrayList<K> stored = state.getParcelableArrayList(SELECTION_ENTRIES); |
| if (stored == null) { |
| return null; |
| } |
| |
| Selection<K> selection = new Selection<>(); |
| selection.mSelection.addAll(stored); |
| return selection; |
| } |
| |
| @Override |
| public @NonNull Bundle asBundle(@NonNull Selection<K> selection) { |
| |
| Bundle bundle = new Bundle(); |
| bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName()); |
| |
| ArrayList<K> value = new ArrayList<>(selection.size()); |
| value.addAll(selection.mSelection); |
| bundle.putParcelableArrayList(SELECTION_ENTRIES, value); |
| |
| return bundle; |
| } |
| } |
| } |