package proton.inject.state;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import proton.inject.ProvisionException;
import proton.inject.util.InjectorUtils;
import proton.inject.util.SparseClassArray;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.SparseArray;
public class StateManager {
private Map<Context, Bundle> mSavedStateBundles = new WeakHashMap<Context, Bundle>();
private Map<Context, List<State>> mSaveStates = new WeakHashMap<Context, List<State>>();
private static final SparseClassArray<Dispatcher> sDispatchers = new SparseClassArray<Dispatcher>();
static {
Dispatcher charSequenceDispatcher = new Dispatcher() {
@Override
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putCharSequence(key, (CharSequence) value);
}
};
Dispatcher charSequenceArrayDispatcher = new Dispatcher() {
@Override
@TargetApi(8)
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putCharSequenceArray(key, (CharSequence[]) value);
}
};
Dispatcher parcelableDispatcher = new Dispatcher() {
@Override
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putParcelable(key, (Parcelable) value);
}
};
Dispatcher parcelableArrayDispatcher = new Dispatcher() {
@Override
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putParcelableArray(key, (Parcelable[]) value);
}
};
Dispatcher serializableDispatcher = new Dispatcher() {
@Override
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putSerializable(key, (Serializable) value);
}
};
Dispatcher sparseParcelableArrayDispatcher = new Dispatcher() {
@SuppressWarnings("unchecked")
@Override
public void dispatch(Bundle bundle, String key, Object value) {
bundle.putSparseParcelableArray(key, (SparseArray<Parcelable>) value);
}
};
sDispatchers.put(byte.class, serializableDispatcher);
sDispatchers.put(boolean.class, serializableDispatcher);
sDispatchers.put(char.class, serializableDispatcher);
sDispatchers.put(short.class, serializableDispatcher);
sDispatchers.put(int.class, serializableDispatcher);
sDispatchers.put(long.class, serializableDispatcher);
sDispatchers.put(float.class, serializableDispatcher);
sDispatchers.put(double.class, serializableDispatcher);
sDispatchers.put(CharSequence.class, charSequenceDispatcher);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)
sDispatchers.put(CharSequence[].class, charSequenceArrayDispatcher);
sDispatchers.put(Parcelable.class, parcelableDispatcher);
sDispatchers.put(Parcelable[].class, parcelableArrayDispatcher);
sDispatchers.put(Serializable.class, serializableDispatcher);
sDispatchers.put(SparseArray.class, sparseParcelableArrayDispatcher);
}
public void registerSavedStateBundle(Context context, Bundle bundle) {
synchronized (this) {
mSavedStateBundles.put(context, bundle);
List<State> list = mSaveStates.get(context);
if (list == null)
return;
for (State state : list) {
Object value = bundle.get(key(state.receiver, state.field));
if (value == null && state.field.getType().isPrimitive())
continue;
InjectorUtils.setField(state.receiver, state.field, value);
}
}
}
public void registerAndRestore(Context context, Object receiver, Field field) {
synchronized (this) {
register(context, receiver, field);
restore(context, receiver, field);
}
}
private void register(Context context, Object receiver, Field field) {
List<State> list = mSaveStates.get(context);
if (list == null) {
list = new ArrayList<State>();
mSaveStates.put(context, list);
}
list.add(new State(receiver, field));
}
private void restore(Context context, Object receiver, Field field) {
Bundle bundle = mSavedStateBundles.get(context);
if (bundle == null)
return;
Object value = bundle.get(key(receiver, field));
if (value == null)
return;
InjectorUtils.setField(receiver, field, value);
}
public void store(Context context, Bundle outState) {
synchronized (this) {
List<State> list = mSaveStates.get(context);
if (list == null)
return;
for (State state : list) {
Class<?> type = state.field.getType();
Dispatcher dispatcher;
if (Parcelable.class.isAssignableFrom(type))
dispatcher = sDispatchers.get(Parcelable.class);
else if (Parcelable[].class.isAssignableFrom(type))
dispatcher = sDispatchers.get(Parcelable[].class);
else if (Serializable.class.isAssignableFrom(type))
dispatcher = sDispatchers.get(Serializable.class);
else if (SparseArray.class.isAssignableFrom(type))
dispatcher = sDispatchers.get(SparseArray.class);
else
dispatcher = sDispatchers.get(type);
if (dispatcher == null)
throw new ProvisionException("Unsupported type: " + type.getName());
Object value = InjectorUtils.getField(state.receiver, state.field);
dispatcher.dispatch(outState, key(state.receiver, state.field), value);
}
}
}
public void destroy(Context context) {
synchronized (this) {
mSaveStates.remove(context);
mSavedStateBundles.remove(context);
}
}
private String key(Object receiver, Field field) {
return "_" + receiver.getClass().getName() + "." + field.getName();
}
private static class State {
private final Object receiver;
private final Field field;
State(Object receiver, Field field) {
this.receiver = receiver;
this.field = field;
}
}
private interface Dispatcher {
public void dispatch(Bundle bundle, String key, Object value);
}
}