/**
* Copyright (c) 2016, 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 com.android.car.stream;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import java.lang.reflect.Array;
/**
* Base class for Parcelable classes that serialize/deserialize themselves using Bundles for
* backward compatibility.
*
*
* By using Bundles which require explicit key-value pairs, unknown fields can be handled
* gracefully. Also, by ensuring that custom classes are serialized to a Bundle containing
* primitives or system classes only, ClassNotFoundExceptions can be prevented when deserializing.
*
*
*
* Subclass must expose a default constructor, implement {@link #writeToBundle(Bundle)} and
* {@link #readFromBundle(Bundle)} instead of {@link #writeToParcel(Parcel, int)}.
* It should also define a CREATOR, as required of all Parcelables, by instantiating a
* {@link BundleableCreator}.
*
* Example:
*
* public static final Creator CREATOR = new BundleableCreator<>(MyClass.class);
*
* @Override
* protected void writeToBundle(Bundle bundle) {
* bundle.putInt(FIRST_FIELD_KEY, mFirstField);
* if (mCustomField != null) {
* Bundle customFieldBundle = new Bundle();
* mCustomField.writeToBundle(customFieldBundle);
* bundle.putBundle(CUSTOM_FIELD_KEY, customFieldBundle);
* }
* bundle.putParcelable(INTENT_KEY, mIntent);
* }
*
* @Override
* protected void readFromBundle(Bundle bundle) {
* mFirstField = bundle.getInt(FIRST_FIELD_KEY);
* Bundle customFieldBundle = bundle.getBundle(CUSTOM_FIELD_KEY);
* if (customFieldBundle != null) {
* mCustomField = new CustomClass();
* mCustomField.readFromBundle(customFieldBundle);
* }
* mIntent = bundle.getParcelable(INTENT_KEY);
* }
*
*
*
* All subclasses should be added to BundleableTest#BUNDLEABLE_CLASSES list to be tested.
*
*/
public abstract class AbstractBundleable implements Parcelable {
private static final String TAG = "Bundleable";
/**
* Creator class for unmarshalling subclasses of {@link AbstractBundleable}.
*/
@VisibleForTesting
public static class BundleableCreator
implements Creator {
private Class clazz;
public BundleableCreator(Class bundleableClazz) {
clazz = bundleableClazz;
}
@Override
public final T createFromParcel(Parcel source) {
T instance = null;
try {
instance = clazz.newInstance();
instance.readFromBundle(source.readBundle());
} catch (Exception e) {
Log.e(TAG, "Failed to instantiate " + clazz.getSimpleName(), e);
}
return instance;
}
@SuppressWarnings("unchecked")
@Override
public final T[] newArray(int size) {
return (T[]) Array.newInstance(clazz, size);
}
}
@Override
public final int describeContents() {
return 0;
}
@Override
public final void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
writeToBundle(bundle);
dest.writeBundle(bundle);
}
@Override
public String toString() {
Bundle bundle = new Bundle();
writeToBundle(bundle);
return bundle.toString();
}
/**
* Writes the states of the instance to the given Bundle. Only primitives or system classes
* can be written into the Bundle. If a field of a custom class needs to be serialized,
* serialize it into a new Bundle, and then write that Bundle into the outer Bundle. A list or
* array of custom class instances should similarly be converted into an array of Bundles first.
*/
protected abstract void writeToBundle(Bundle bundle);
/**
* Reads the states saved in the Bundle into the current instance. The implementation should
* mirror that of {@link #writeToBundle(Bundle)}.
*/
protected abstract void readFromBundle(Bundle bundle);
}