Add Bundleable interface

It's for classes to clearly denote they support bundling and it gives
us a good place to document the best practice to implement fromBundle.

PiperOrigin-RevId: 355609942
This commit is contained in:
gyumin 2021-02-04 14:03:21 +00:00 committed by Oliver Woodman
parent 35d34af2e9
commit d72d25470d
5 changed files with 86 additions and 30 deletions

View file

@ -0,0 +1,52 @@
/*
* Copyright 2021 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.google.android.exoplayer2;
import android.os.Bundle;
/**
* Interface for classes whose instance can be stored in a {@link Bundle} by {@link #toBundle()} and
* can be restored from the {@link Bundle} by using the static {@code CREATOR} field that implements
* {@link Bundleable.Creator}.
*
* <p>For example, a {@link Bundleable} class {@code Foo} supports the following:
*
* <pre>{@code
* Foo foo = ...;
* Bundle fooBundle = foo.toBundle();
* Foo restoredFoo = Foo.CREATOR.fromBundle(fooBundle);
* assertThat(restoredFoo).isEqualTo(foo);
* }</pre>
*/
public interface Bundleable {
/** Returns a {@link Bundle} representing the information stored in this object. */
Bundle toBundle();
/** Interface for the static {@code CREATOR} field of {@link Bundleable} classes. */
interface Creator<T extends Bundleable> {
/**
* Restores a {@link Bundleable} instance from a {@link Bundle} produced by {@link
* Bundleable#toBundle()}.
*
* <p>It guarantees the compatibility of {@link Bundle} representations produced by different
* versions of {@link Bundleable#toBundle()} by providing best default values for missing
* fields. It may throw an {@link IllegalArgumentException} if any essential fields are missing.
*/
T fromBundle(Bundle bundle);
}
}

View file

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.audio;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Util;
@ -32,7 +33,7 @@ import com.google.android.exoplayer2.util.Util;
* <p>This class is based on {@link android.media.AudioAttributes}, but can be used on all supported
* API versions.
*/
public final class AudioAttributes {
public final class AudioAttributes implements Bundleable {
private static final String FIELD_CONTENT_TYPE = "contentType";
private static final String FIELD_FLAGS = "flags";
@ -165,7 +166,7 @@ public final class AudioAttributes {
return result;
}
/** Converts this instance into a {@link Bundle}. */
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putInt(FIELD_CONTENT_TYPE, contentType);
@ -175,21 +176,21 @@ public final class AudioAttributes {
return bundle;
}
/** Creates an {@link AudioAttributes} instance from a {@link Bundle}. */
public static AudioAttributes fromBundle(Bundle bundle) {
Builder builder = new Builder();
if (bundle.containsKey(FIELD_CONTENT_TYPE)) {
builder.setContentType(bundle.getInt(FIELD_CONTENT_TYPE));
}
if (bundle.containsKey(FIELD_FLAGS)) {
builder.setFlags(bundle.getInt(FIELD_FLAGS));
}
if (bundle.containsKey(FIELD_USAGE)) {
builder.setUsage(bundle.getInt(FIELD_USAGE));
}
if (bundle.containsKey(FIELD_ALLOWED_CAPTURE_POLICY)) {
builder.setAllowedCapturePolicy(bundle.getInt(FIELD_ALLOWED_CAPTURE_POLICY));
}
return builder.build();
}
public static final Creator<AudioAttributes> CREATOR =
bundle -> {
Builder builder = new Builder();
if (bundle.containsKey(FIELD_CONTENT_TYPE)) {
builder.setContentType(bundle.getInt(FIELD_CONTENT_TYPE));
}
if (bundle.containsKey(FIELD_FLAGS)) {
builder.setFlags(bundle.getInt(FIELD_FLAGS));
}
if (bundle.containsKey(FIELD_USAGE)) {
builder.setUsage(bundle.getInt(FIELD_USAGE));
}
if (bundle.containsKey(FIELD_ALLOWED_CAPTURE_POLICY)) {
builder.setAllowedCapturePolicy(bundle.getInt(FIELD_ALLOWED_CAPTURE_POLICY));
}
return builder.build();
};
}

View file

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.device;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@ -25,7 +26,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Information about the playback device. */
public final class DeviceInfo {
public final class DeviceInfo implements Bundleable {
private static final String FIELD_PLAYBACK_TYPE = "playbackType";
private static final String FIELD_MIN_VOLUME = "minVolume";
@ -86,7 +87,7 @@ public final class DeviceInfo {
return result;
}
/** Converts this instance into a {@link Bundle}. */
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putInt(FIELD_PLAYBACK_TYPE, playbackType);
@ -95,11 +96,12 @@ public final class DeviceInfo {
return bundle;
}
/** Creates an {@link DeviceInfo} instance from a {@link Bundle}. */
public static DeviceInfo fromBundle(Bundle bundle) {
int playbackType = bundle.getInt(FIELD_PLAYBACK_TYPE, /* defaultValue= */ PLAYBACK_TYPE_LOCAL);
int minVolume = bundle.getInt(FIELD_MIN_VOLUME, /* defaultValue= */ 0);
int maxVolume = bundle.getInt(FIELD_MAX_VOLUME, /* defaultValue= */ 0);
return new DeviceInfo(playbackType, minVolume, maxVolume);
}
public static final Creator<DeviceInfo> CREATOR =
bundle -> {
int playbackType =
bundle.getInt(FIELD_PLAYBACK_TYPE, /* defaultValue= */ PLAYBACK_TYPE_LOCAL);
int minVolume = bundle.getInt(FIELD_MIN_VOLUME, /* defaultValue= */ 0);
int maxVolume = bundle.getInt(FIELD_MAX_VOLUME, /* defaultValue= */ 0);
return new DeviceInfo(playbackType, minVolume, maxVolume);
};
}

View file

@ -36,6 +36,7 @@ public class AudioAttributesTest {
.setAllowedCapturePolicy(C.ALLOW_CAPTURE_BY_SYSTEM)
.build();
assertThat(AudioAttributes.fromBundle(audioAttributes.toBundle())).isEqualTo(audioAttributes);
assertThat(AudioAttributes.CREATOR.fromBundle(audioAttributes.toBundle()))
.isEqualTo(audioAttributes);
}
}

View file

@ -30,6 +30,6 @@ public class DeviceInfoTest {
DeviceInfo deviceInfo =
new DeviceInfo(DeviceInfo.PLAYBACK_TYPE_REMOTE, /* minVolume= */ 1, /* maxVolume= */ 9);
assertThat(DeviceInfo.fromBundle(deviceInfo.toBundle())).isEqualTo(deviceInfo);
assertThat(DeviceInfo.CREATOR.fromBundle(deviceInfo.toBundle())).isEqualTo(deviceInfo);
}
}