/* * 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)}. * *

* See * {@link androidx.recyclerview.selection.SelectionTracker.Builder SelectionTracker.Builder} * for more detailed advice on which key type to use for your selection keys. * * @param 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 { @VisibleForTesting static final String SELECTION_ENTRIES = "androidx.recyclerview.selection.entries"; @VisibleForTesting static final String SELECTION_KEY_TYPE = "androidx.recyclerview.selection.type"; private final Class mType; /** * Creates a new instance. * * @param type the key type class that is being used. */ public StorageStrategy(@NonNull Class 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 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 selection); String getKeyTypeName() { return mType.getCanonicalName(); } /** * @return StorageStrategy suitable for use with {@link Parcelable} keys * (like {@link android.net.Uri}). */ public static StorageStrategy createParcelableStorage(Class type) { return new ParcelableStorageStrategy(type); } /** * @return StorageStrategy suitable for use with {@link String} keys. */ public static StorageStrategy createStringStorage() { return new StringStorageStrategy(); } /** * @return StorageStrategy suitable for use with {@link Long} keys. */ public static StorageStrategy createLongStorage() { return new LongStorageStrategy(); } private static class StringStorageStrategy extends StorageStrategy { StringStorageStrategy() { super(String.class); } @Override public @Nullable Selection asSelection(@NonNull Bundle state) { String keyType = state.getString(SELECTION_KEY_TYPE, null); if (keyType == null || !keyType.equals(getKeyTypeName())) { return null; } @Nullable ArrayList stored = state.getStringArrayList(SELECTION_ENTRIES); if (stored == null) { return null; } Selection selection = new Selection<>(); selection.mSelection.addAll(stored); return selection; } @Override public @NonNull Bundle asBundle(@NonNull Selection selection) { Bundle bundle = new Bundle(); bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName()); ArrayList value = new ArrayList<>(selection.size()); value.addAll(selection.mSelection); bundle.putStringArrayList(SELECTION_ENTRIES, value); return bundle; } } private static class LongStorageStrategy extends StorageStrategy { LongStorageStrategy() { super(Long.class); } @Override public @Nullable Selection 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 selection = new Selection<>(); for (long key : stored) { selection.mSelection.add(key); } return selection; } @Override public @NonNull Bundle asBundle(@NonNull Selection 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 extends StorageStrategy { ParcelableStorageStrategy(Class type) { super(type); checkArgument(Parcelable.class.isAssignableFrom(type)); } @Override public @Nullable Selection asSelection(@NonNull Bundle state) { String keyType = state.getString(SELECTION_KEY_TYPE, null); if (keyType == null || !keyType.equals(getKeyTypeName())) { return null; } @Nullable ArrayList stored = state.getParcelableArrayList(SELECTION_ENTRIES); if (stored == null) { return null; } Selection selection = new Selection<>(); selection.mSelection.addAll(stored); return selection; } @Override public @NonNull Bundle asBundle(@NonNull Selection selection) { Bundle bundle = new Bundle(); bundle.putString(SELECTION_KEY_TYPE, getKeyTypeName()); ArrayList value = new ArrayList<>(selection.size()); value.addAll(selection.mSelection); bundle.putParcelableArrayList(SELECTION_ENTRIES, value); return bundle; } } }