Implement Bundleable for AdPlaybackState

PiperOrigin-RevId: 361731578
This commit is contained in:
gyumin 2021-03-09 06:02:57 +00:00 committed by Ian Baker
parent bc9fb8615e
commit 6f9dbfe0c1
2 changed files with 125 additions and 27 deletions

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.ads;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static java.lang.Math.max;
import android.net.Uri;
@ -24,7 +25,6 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -39,7 +39,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
* <p>Instances are immutable. Call the {@code with*} methods to get new instances that have the
* required changes.
*/
public final class AdPlaybackState {
public final class AdPlaybackState implements Bundleable {
/**
* Represents a group of ads, with information about their states.
@ -69,7 +69,7 @@ public final class AdPlaybackState {
private AdGroup(
int count, @AdState int[] states, @NullableType Uri[] uris, long[] durationsUs) {
Assertions.checkArgument(states.length == uris.length);
checkArgument(states.length == uris.length);
this.count = count;
this.states = states;
this.uris = uris;
@ -165,9 +165,9 @@ public final class AdPlaybackState {
*/
@CheckResult
public AdGroup withAdState(@AdState int state, int index) {
Assertions.checkArgument(count == C.LENGTH_UNSET || index < count);
checkArgument(count == C.LENGTH_UNSET || index < count);
@AdState int[] states = copyStatesWithSpaceForAdCount(this.states, index + 1);
Assertions.checkArgument(
checkArgument(
states[index] == AD_STATE_UNAVAILABLE
|| states[index] == AD_STATE_AVAILABLE
|| states[index] == state);
@ -260,28 +260,24 @@ public final class AdPlaybackState {
}
/** Object that can restore {@link AdGroup} from a {@link Bundle}. */
public static final Creator<AdGroup> CREATOR =
new Creator<AdGroup>() {
public static final Creator<AdGroup> CREATOR = AdGroup::fromBundle;
// getParcelableArrayList may have null elements.
@SuppressWarnings("nullness:type.argument.type.incompatible")
@Override
public AdGroup fromBundle(Bundle bundle) {
int count = bundle.getInt(keyForField(FIELD_COUNT), /* defaultValue= */ C.LENGTH_UNSET);
@Nullable
ArrayList<@NullableType Uri> uriList =
bundle.getParcelableArrayList(keyForField(FIELD_URIS));
@Nullable
@AdState
int[] states = bundle.getIntArray(keyForField(FIELD_STATES));
@Nullable long[] durationsUs = bundle.getLongArray(keyForField(FIELD_DURATIONS_US));
return new AdGroup(
count,
states == null ? new int[0] : states,
uriList == null ? new Uri[0] : uriList.toArray(new Uri[0]),
durationsUs == null ? new long[0] : durationsUs);
}
};
// getParcelableArrayList may have null elements.
@SuppressWarnings("nullness:type.argument.type.incompatible")
private static AdGroup fromBundle(Bundle bundle) {
int count = bundle.getInt(keyForField(FIELD_COUNT), /* defaultValue= */ C.LENGTH_UNSET);
@Nullable
ArrayList<@NullableType Uri> uriList = bundle.getParcelableArrayList(keyForField(FIELD_URIS));
@Nullable
@AdState
int[] states = bundle.getIntArray(keyForField(FIELD_STATES));
@Nullable long[] durationsUs = bundle.getLongArray(keyForField(FIELD_DURATIONS_US));
return new AdGroup(
count,
states == null ? new int[0] : states,
uriList == null ? new Uri[0] : uriList.toArray(new Uri[0]),
durationsUs == null ? new long[0] : durationsUs);
}
private static String keyForField(@AdGroup.FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
@ -368,6 +364,7 @@ public final class AdPlaybackState {
@Nullable AdGroup[] adGroups,
long adResumePositionUs,
long contentDurationUs) {
checkArgument(adGroups == null || adGroups.length == adGroupTimesUs.length);
this.adsId = adsId;
this.adGroupTimesUs = adGroupTimesUs;
this.adResumePositionUs = adResumePositionUs;
@ -449,7 +446,7 @@ public final class AdPlaybackState {
*/
@CheckResult
public AdPlaybackState withAdCount(int adGroupIndex, int adCount) {
Assertions.checkArgument(adCount > 0);
checkArgument(adCount > 0);
if (adGroups[adGroupIndex].count == adCount) {
return this;
}
@ -634,4 +631,79 @@ public final class AdPlaybackState {
return positionUs < adGroupPositionUs;
}
}
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_AD_GROUP_TIMES_US,
FIELD_AD_GROUPS,
FIELD_AD_RESUME_POSITION_US,
FIELD_CONTENT_DURATION_US
})
private @interface FieldNumber {}
private static final int FIELD_AD_GROUP_TIMES_US = 1;
private static final int FIELD_AD_GROUPS = 2;
private static final int FIELD_AD_RESUME_POSITION_US = 3;
private static final int FIELD_CONTENT_DURATION_US = 4;
/**
* {@inheritDoc}
*
* <p>It omits the {@link #adsId} field so the {@link #adsId} of instances restored by {@link
* #CREATOR} will always be {@code null}.
*/
// TODO(b/166765820): See if missing adsId would be okay and add adsId to the Bundle otherwise.
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putLongArray(keyForField(FIELD_AD_GROUP_TIMES_US), adGroupTimesUs);
ArrayList<Bundle> adGroupBundleList = new ArrayList<>();
for (AdGroup adGroup : adGroups) {
adGroupBundleList.add(adGroup.toBundle());
}
bundle.putParcelableArrayList(keyForField(FIELD_AD_GROUPS), adGroupBundleList);
bundle.putLong(keyForField(FIELD_AD_RESUME_POSITION_US), adResumePositionUs);
bundle.putLong(keyForField(FIELD_CONTENT_DURATION_US), contentDurationUs);
return bundle;
}
/**
* Object that can restore {@link AdPlaybackState} from a {@link Bundle}.
*
* <p>The {@link #adsId} of restored instances will always be {@code null}.
*/
public static final Bundleable.Creator<AdPlaybackState> CREATOR = AdPlaybackState::fromBundle;
private static AdPlaybackState fromBundle(Bundle bundle) {
@Nullable long[] adGroupTimesUs = bundle.getLongArray(keyForField(FIELD_AD_GROUP_TIMES_US));
@Nullable
ArrayList<Bundle> adGroupBundleList =
bundle.getParcelableArrayList(keyForField(FIELD_AD_GROUPS));
@Nullable AdGroup[] adGroups;
if (adGroupBundleList == null) {
adGroups = null;
} else {
adGroups = new AdGroup[adGroupBundleList.size()];
for (int i = 0; i < adGroupBundleList.size(); i++) {
adGroups[i] = AdGroup.CREATOR.fromBundle(adGroupBundleList.get(i));
}
}
long adResumePositionUs =
bundle.getLong(keyForField(FIELD_AD_RESUME_POSITION_US), /* defaultValue= */ 0);
long contentDurationUs =
bundle.getLong(keyForField(FIELD_CONTENT_DURATION_US), /* defaultValue= */ C.TIME_UNSET);
return new AdPlaybackState(
/* adsId= */ null,
adGroupTimesUs == null ? new long[0] : adGroupTimesUs,
adGroups,
adResumePositionUs,
contentDurationUs);
}
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View file

@ -149,6 +149,32 @@ public class AdPlaybackStateTest {
assertThat(state.adGroups[1].count).isEqualTo(0);
}
@Test
public void roundtripViaBundle_yieldsEqualFieldsExceptAdsId() {
AdPlaybackState originalState =
state
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2)
.withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0)
.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1)
.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI)
.withAdDurationsUs(new long[][] {{12}, {34, 56}})
.withAdResumePositionUs(123)
.withContentDurationUs(456);
AdPlaybackState restoredState = AdPlaybackState.CREATOR.fromBundle(originalState.toBundle());
assertThat(restoredState.adsId).isNull();
assertThat(restoredState.adGroupCount).isEqualTo(originalState.adGroupCount);
assertThat(restoredState.adGroupTimesUs).isEqualTo(originalState.adGroupTimesUs);
assertThat(restoredState.adGroups).isEqualTo(originalState.adGroups);
assertThat(restoredState.adResumePositionUs).isEqualTo(originalState.adResumePositionUs);
assertThat(restoredState.contentDurationUs).isEqualTo(originalState.contentDurationUs);
}
@Test
public void roundtripViaBundle_ofAdGroup_yieldsEqualInstance() {
AdPlaybackState.AdGroup adGroup =