blob: 3418d1f8708080376642b4259c1ea2b3480c6ab0 [file] [log] [blame]
/*
* 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.widget.recyclerview.selection;
import static android.support.v4.util.Preconditions.checkArgument;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import java.util.ArrayList;
/**
* Strategy for storing keys in saved state. Extend this class when using custom
* key types that aren't parcelable. Prefer use of builtin storage strategies:
* {@link #createStringStorage()}, {@link #createLongStorage()},
* {@link #createParcelableStorage(Class)}.
*
* @see androidx.widget.recyclerview.selection.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.widget.recyclerview.selection.entries";
@VisibleForTesting
static final String SELECTION_KEY_TYPE = "androidx.widget.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;
}
}
}