Implement Bundleable for Timeline
PiperOrigin-RevId: 362474276
This commit is contained in:
parent
3f4f2f90b5
commit
f8fb9dd606
@ -32,6 +32,7 @@ project.ext {
|
|||||||
androidxAnnotationVersion = '1.1.0'
|
androidxAnnotationVersion = '1.1.0'
|
||||||
androidxAppCompatVersion = '1.1.0'
|
androidxAppCompatVersion = '1.1.0'
|
||||||
androidxCollectionVersion = '1.1.0'
|
androidxCollectionVersion = '1.1.0'
|
||||||
|
androidxCoreVersion = '1.3.2'
|
||||||
androidxFuturesVersion = '1.1.0'
|
androidxFuturesVersion = '1.1.0'
|
||||||
androidxMediaVersion = '1.2.1'
|
androidxMediaVersion = '1.2.1'
|
||||||
androidxMedia2Version = '1.1.2'
|
androidxMedia2Version = '1.1.2'
|
||||||
@ -42,6 +43,7 @@ project.ext {
|
|||||||
androidxTestRunnerVersion = '1.3.0'
|
androidxTestRunnerVersion = '1.3.0'
|
||||||
androidxTestRulesVersion = '1.3.0'
|
androidxTestRulesVersion = '1.3.0'
|
||||||
androidxTestServicesStorageVersion = '1.3.0'
|
androidxTestServicesStorageVersion = '1.3.0'
|
||||||
|
androidxTestTruthVersion = '1.3.0'
|
||||||
truthVersion = '1.0'
|
truthVersion = '1.0'
|
||||||
modulePrefix = ':'
|
modulePrefix = ':'
|
||||||
if (gradle.ext.has('exoplayerModulePrefix')) {
|
if (gradle.ext.has('exoplayerModulePrefix')) {
|
||||||
|
@ -26,6 +26,7 @@ dependencies {
|
|||||||
exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations'
|
exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations'
|
||||||
}
|
}
|
||||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||||
|
implementation 'androidx.core:core:' + androidxCoreVersion
|
||||||
compileOnly 'com.google.code.findbugs:jsr305:' + jsr305Version
|
compileOnly 'com.google.code.findbugs:jsr305:' + jsr305Version
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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 static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Binder} to transfer a list of {@link Bundle Bundles} across processes by splitting the
|
||||||
|
* list into multiple transactions.
|
||||||
|
*
|
||||||
|
* <p>Note: Using this class causes synchronous binder calls in the opposite direction regardless of
|
||||||
|
* the "oneway" property.
|
||||||
|
*
|
||||||
|
* <p>Example usage:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* // Sender
|
||||||
|
* List<Bundle> list = ...;
|
||||||
|
* IBinder binder = new BundleListRetriever(list);
|
||||||
|
* Bundle bundle = new Bundle();
|
||||||
|
* bundle.putBinder("list", binder);
|
||||||
|
*
|
||||||
|
* // Receiver
|
||||||
|
* Bundle bundle = ...; // Received from the sender
|
||||||
|
* IBinder binder = bundle.getBinder("list");
|
||||||
|
* List<Bundle> list = BundleListRetriever.getList(binder);
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public final class BundleListRetriever extends Binder {
|
||||||
|
|
||||||
|
// Soft limit of an IPC buffer size
|
||||||
|
private static final int SUGGESTED_MAX_IPC_SIZE =
|
||||||
|
Util.SDK_INT >= 30 ? IBinder.getSuggestedMaxIpcSizeBytes() : 64 * 1024;
|
||||||
|
|
||||||
|
private static final int REPLY_END_OF_LIST = 0;
|
||||||
|
private static final int REPLY_CONTINUE = 1;
|
||||||
|
private static final int REPLY_BREAK = 2;
|
||||||
|
|
||||||
|
private final ImmutableList<Bundle> list;
|
||||||
|
|
||||||
|
/** Creates a {@link Binder} to send a list of {@link Bundle Bundles} to another process. */
|
||||||
|
public BundleListRetriever(List<Bundle> list) {
|
||||||
|
this.list = ImmutableList.copyOf(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onTransact(int code, Parcel data, @Nullable Parcel reply, int flags)
|
||||||
|
throws RemoteException {
|
||||||
|
if (code != FIRST_CALL_TRANSACTION) {
|
||||||
|
return super.onTransact(code, data, reply, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = list.size();
|
||||||
|
int index = data.readInt();
|
||||||
|
while (index < count && reply.dataSize() < SUGGESTED_MAX_IPC_SIZE) {
|
||||||
|
reply.writeInt(REPLY_CONTINUE);
|
||||||
|
reply.writeBundle(list.get(index));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
reply.writeInt(index < count ? REPLY_BREAK : REPLY_END_OF_LIST);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of {@link Bundle Bundles} from a {@link BundleListRetriever}.
|
||||||
|
*
|
||||||
|
* @param binder A binder interface backed by {@link BundleListRetriever}.
|
||||||
|
* @return The list of {@link Bundle Bundles}.
|
||||||
|
*/
|
||||||
|
public static ImmutableList<Bundle> getList(IBinder binder) {
|
||||||
|
ImmutableList.Builder<Bundle> builder = ImmutableList.builder();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
int replyCode = REPLY_CONTINUE;
|
||||||
|
|
||||||
|
while (replyCode != REPLY_END_OF_LIST) {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
data.writeInt(index);
|
||||||
|
try {
|
||||||
|
binder.transact(FIRST_CALL_TRANSACTION, data, reply, /* flags= */ 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
while ((replyCode = reply.readInt()) == REPLY_CONTINUE) {
|
||||||
|
builder.add(checkNotNull(reply.readBundle()));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reply.recycle();
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
@ -19,16 +19,21 @@ import static com.google.android.exoplayer2.util.Assertions.checkState;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.BundleCompat;
|
||||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flexible representation of the structure of media. A timeline is able to represent the
|
* A flexible representation of the structure of media. A timeline is able to represent the
|
||||||
@ -124,7 +129,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
* <p>This case includes mid-roll ad groups, which are defined as part of the timeline's single
|
* <p>This case includes mid-roll ad groups, which are defined as part of the timeline's single
|
||||||
* period. The period can be queried for information about the ad groups and the ads they contain.
|
* period. The period can be queried for information about the ad groups and the ads they contain.
|
||||||
*/
|
*/
|
||||||
public abstract class Timeline {
|
public abstract class Timeline implements Bundleable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds information about a window in a {@link Timeline}. A window usually corresponds to one
|
* Holds information about a window in a {@link Timeline}. A window usually corresponds to one
|
||||||
@ -1245,4 +1250,148 @@ public abstract class Timeline {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bundleable implementation.
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({FIELD_WINDOWS, FIELD_PERIODS})
|
||||||
|
private @interface FieldNumber {}
|
||||||
|
|
||||||
|
private static final int FIELD_WINDOWS = 0;
|
||||||
|
private static final int FIELD_PERIODS = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The {@link #getWindow(int, Window)} windows} and {@link #getPeriod(int, Period) periods} of
|
||||||
|
* an instance restored by {@link #CREATOR} may have missing fields as described in {@link
|
||||||
|
* Window#toBundle()} and {@link Period#toBundle()}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final Bundle toBundle() {
|
||||||
|
List<Bundle> windowBundles = new ArrayList<>();
|
||||||
|
int windowCount = getWindowCount();
|
||||||
|
for (int i = 0; i < windowCount; i++) {
|
||||||
|
Window window = new Window();
|
||||||
|
getWindow(i, window, /* defaultPositionProjectionUs= */ 0);
|
||||||
|
windowBundles.add(window.toBundle());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Bundle> periodBundles = new ArrayList<>();
|
||||||
|
int periodCount = getPeriodCount();
|
||||||
|
for (int i = 0; i < periodCount; i++) {
|
||||||
|
Period period = new Period();
|
||||||
|
getPeriod(i, period, /* setIds= */ false);
|
||||||
|
periodBundles.add(period.toBundle());
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
BundleCompat.putBinder(
|
||||||
|
bundle, keyForField(FIELD_WINDOWS), new BundleListRetriever(windowBundles));
|
||||||
|
BundleCompat.putBinder(
|
||||||
|
bundle, keyForField(FIELD_PERIODS), new BundleListRetriever(periodBundles));
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that can restore a {@link Timeline} from a {@link Bundle}.
|
||||||
|
*
|
||||||
|
* <p>The {@link #getWindow(int, Window)} windows} and {@link #getPeriod(int, Period) periods} of
|
||||||
|
* a restored instance may have missing fields as described in {@link Window#CREATOR} and {@link
|
||||||
|
* Period#CREATOR}.
|
||||||
|
*/
|
||||||
|
public static final Creator<Timeline> CREATOR = Timeline::fromBundle;
|
||||||
|
|
||||||
|
private static Timeline fromBundle(Bundle bundle) {
|
||||||
|
ImmutableList<Window> windows =
|
||||||
|
fromBundleListRetriever(
|
||||||
|
Window.CREATOR, BundleCompat.getBinder(bundle, keyForField(FIELD_WINDOWS)));
|
||||||
|
ImmutableList<Period> periods =
|
||||||
|
fromBundleListRetriever(
|
||||||
|
Period.CREATOR, BundleCompat.getBinder(bundle, keyForField(FIELD_PERIODS)));
|
||||||
|
return new RemotableTimeline(windows, periods);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Bundleable> ImmutableList<T> fromBundleListRetriever(
|
||||||
|
Creator<T> creator, @Nullable IBinder binder) {
|
||||||
|
if (binder == null) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
ImmutableList.Builder<T> builder = new ImmutableList.Builder<>();
|
||||||
|
List<Bundle> bundleList = BundleListRetriever.getList(binder);
|
||||||
|
for (int i = 0; i < bundleList.size(); i++) {
|
||||||
|
builder.add(creator.fromBundle(bundleList.get(i)));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String keyForField(@FieldNumber int field) {
|
||||||
|
return Integer.toString(field, Character.MAX_RADIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concrete class of {@link Timeline} to restore a {@link Timeline} instance from a {@link
|
||||||
|
* Bundle} sent by another process via {@link IBinder}.
|
||||||
|
*/
|
||||||
|
private static final class RemotableTimeline extends Timeline {
|
||||||
|
|
||||||
|
private final ImmutableList<Window> windows;
|
||||||
|
private final ImmutableList<Period> periods;
|
||||||
|
|
||||||
|
public RemotableTimeline(ImmutableList<Window> windows, ImmutableList<Period> periods) {
|
||||||
|
this.windows = windows;
|
||||||
|
this.periods = periods;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWindowCount() {
|
||||||
|
return windows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Window getWindow(
|
||||||
|
int windowIndex, Window window, long ignoredDefaultPositionProjectionUs) {
|
||||||
|
Window w = windows.get(windowIndex);
|
||||||
|
window.set(
|
||||||
|
w.uid,
|
||||||
|
w.mediaItem,
|
||||||
|
w.manifest,
|
||||||
|
w.presentationStartTimeMs,
|
||||||
|
w.windowStartTimeMs,
|
||||||
|
w.elapsedRealtimeEpochOffsetMs,
|
||||||
|
w.isSeekable,
|
||||||
|
w.isDynamic,
|
||||||
|
w.liveConfiguration,
|
||||||
|
w.defaultPositionUs,
|
||||||
|
w.durationUs,
|
||||||
|
w.firstPeriodIndex,
|
||||||
|
w.lastPeriodIndex,
|
||||||
|
w.positionInFirstPeriodUs);
|
||||||
|
window.isPlaceholder = w.isPlaceholder;
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPeriodCount() {
|
||||||
|
return periods.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Period getPeriod(int periodIndex, Period period, boolean ignoredSetIds) {
|
||||||
|
Period p = periods.get(periodIndex);
|
||||||
|
return period.set(
|
||||||
|
p.id, p.uid, p.windowIndex, p.durationUs, p.positionInWindowUs, p.adPlaybackState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIndexOfPeriod(Object uid) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getUidOfPeriod(int periodIndex) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.ext.truth.os.BundleSubject;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Tests for {@link BundleListRetriever}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class BundleListRetrieverTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getList_preservedLargeList() {
|
||||||
|
int count = 100_000;
|
||||||
|
List<Bundle> listBefore = new ArrayList<>();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt("i", i);
|
||||||
|
listBefore.add(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Bundle> listAfter = BundleListRetriever.getList(new BundleListRetriever(listBefore));
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Bundle bundle = listAfter.get(i);
|
||||||
|
BundleSubject.assertThat(bundle).integer("i").isEqualTo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
||||||
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
@ -201,6 +202,41 @@ public class TimelineTest {
|
|||||||
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
|
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void roundtripViaBundle_ofTimeline_yieldsEqualInstanceExceptIdsAndManifest() {
|
||||||
|
Timeline timeline =
|
||||||
|
new FakeTimeline(
|
||||||
|
new TimelineWindowDefinition(
|
||||||
|
/* periodCount= */ 2,
|
||||||
|
/* id= */ new Object(),
|
||||||
|
/* isSeekable= */ true,
|
||||||
|
/* isDynamic= */ true,
|
||||||
|
/* isLive= */ true,
|
||||||
|
/* isPlaceholder= */ false,
|
||||||
|
/* durationUs= */ 2,
|
||||||
|
/* defaultPositionUs= */ 22,
|
||||||
|
/* windowOffsetInFirstPeriodUs= */ 222,
|
||||||
|
AdPlaybackState.NONE,
|
||||||
|
new MediaItem.Builder().setMediaId("mediaId2").build()),
|
||||||
|
new TimelineWindowDefinition(
|
||||||
|
/* periodCount= */ 3,
|
||||||
|
/* id= */ new Object(),
|
||||||
|
/* isSeekable= */ true,
|
||||||
|
/* isDynamic= */ true,
|
||||||
|
/* isLive= */ true,
|
||||||
|
/* isPlaceholder= */ false,
|
||||||
|
/* durationUs= */ 3,
|
||||||
|
/* defaultPositionUs= */ 33,
|
||||||
|
/* windowOffsetInFirstPeriodUs= */ 333,
|
||||||
|
AdPlaybackState.NONE,
|
||||||
|
new MediaItem.Builder().setMediaId("mediaId3").build()));
|
||||||
|
|
||||||
|
Timeline restoredTimeline = Timeline.CREATOR.fromBundle(timeline.toBundle());
|
||||||
|
|
||||||
|
TimelineAsserts.assertEqualsExceptIdsAndManifest(
|
||||||
|
/* expectedTimeline= */ timeline, /* actualTimeline= */ restoredTimeline);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() {
|
public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() {
|
||||||
Timeline.Window window = new Timeline.Window();
|
Timeline.Window window = new Timeline.Window();
|
||||||
@ -229,9 +265,8 @@ public class TimelineTest {
|
|||||||
Timeline.Window restoredWindow = Timeline.Window.CREATOR.fromBundle(window.toBundle());
|
Timeline.Window restoredWindow = Timeline.Window.CREATOR.fromBundle(window.toBundle());
|
||||||
|
|
||||||
assertThat(restoredWindow.manifest).isNull();
|
assertThat(restoredWindow.manifest).isNull();
|
||||||
window.uid = restoredWindow.uid;
|
TimelineAsserts.assertWindowEqualsExceptUidAndManifest(
|
||||||
window.manifest = null;
|
/* expectedWindow= */ window, /* actualWindow= */ restoredWindow);
|
||||||
assertThat(restoredWindow).isEqualTo(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -245,9 +280,10 @@ public class TimelineTest {
|
|||||||
|
|
||||||
Timeline.Period restoredPeriod = Timeline.Period.CREATOR.fromBundle(period.toBundle());
|
Timeline.Period restoredPeriod = Timeline.Period.CREATOR.fromBundle(period.toBundle());
|
||||||
|
|
||||||
period.id = null;
|
assertThat(restoredPeriod.id).isNull();
|
||||||
period.uid = null;
|
assertThat(restoredPeriod.uid).isNull();
|
||||||
assertThat(restoredPeriod).isEqualTo(period);
|
TimelineAsserts.assertPeriodEqualsExceptIds(
|
||||||
|
/* expectedPeriod= */ period, /* actualPeriod= */ restoredPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Populates the deprecated window.tag property.
|
@SuppressWarnings("deprecation") // Populates the deprecated window.tag property.
|
||||||
|
@ -17,6 +17,7 @@ dependencies {
|
|||||||
api 'org.mockito:mockito-core:' + mockitoVersion
|
api 'org.mockito:mockito-core:' + mockitoVersion
|
||||||
api 'androidx.test:core:' + androidxTestCoreVersion
|
api 'androidx.test:core:' + androidxTestCoreVersion
|
||||||
api 'androidx.test.ext:junit:' + androidxTestJUnitVersion
|
api 'androidx.test.ext:junit:' + androidxTestJUnitVersion
|
||||||
|
api 'androidx.test.ext:truth:' + androidxTestTruthVersion
|
||||||
api 'junit:junit:' + junitVersion
|
api 'junit:junit:' + junitVersion
|
||||||
api 'com.google.truth:truth:' + truthVersion
|
api 'com.google.truth:truth:' + truthVersion
|
||||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.testutil;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
@ -27,15 +28,13 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
/** Unit test for {@link Timeline}. */
|
/** Assertion methods for {@link Timeline}. */
|
||||||
public final class TimelineAsserts {
|
public final class TimelineAsserts {
|
||||||
|
|
||||||
private static final int[] REPEAT_MODES = {
|
private static final int[] REPEAT_MODES = {
|
||||||
Player.REPEAT_MODE_OFF, Player.REPEAT_MODE_ONE, Player.REPEAT_MODE_ALL
|
Player.REPEAT_MODE_OFF, Player.REPEAT_MODE_ONE, Player.REPEAT_MODE_ALL
|
||||||
};
|
};
|
||||||
|
|
||||||
private TimelineAsserts() {}
|
|
||||||
|
|
||||||
/** Assert that timeline is empty (i.e. has no windows or periods). */
|
/** Assert that timeline is empty (i.e. has no windows or periods). */
|
||||||
public static void assertEmpty(Timeline timeline) {
|
public static void assertEmpty(Timeline timeline) {
|
||||||
assertWindowTags(timeline);
|
assertWindowTags(timeline);
|
||||||
@ -173,4 +172,64 @@ public final class TimelineAsserts {
|
|||||||
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
|
assertThat(period.getAdGroupCount()).isEqualTo(expectedAdGroupCounts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that {@link Timeline timelines} are equal except {@link Window#uid}, {@link
|
||||||
|
* Window#manifest}, {@link Period#id}, and {@link Period#uid}.
|
||||||
|
*/
|
||||||
|
public static void assertEqualsExceptIdsAndManifest(
|
||||||
|
Timeline expectedTimeline, Timeline actualTimeline) {
|
||||||
|
assertThat(actualTimeline.getWindowCount()).isEqualTo(expectedTimeline.getWindowCount());
|
||||||
|
for (int i = 0; i < actualTimeline.getWindowCount(); i++) {
|
||||||
|
Window expectedWindow = new Window();
|
||||||
|
Window actualWindow = new Window();
|
||||||
|
assertWindowEqualsExceptUidAndManifest(
|
||||||
|
expectedTimeline.getWindow(i, expectedWindow, /* defaultPositionProjectionUs= */ 0),
|
||||||
|
actualTimeline.getWindow(i, actualWindow, /* defaultPositionProjectionUs= */ 0));
|
||||||
|
}
|
||||||
|
assertThat(actualTimeline.getPeriodCount()).isEqualTo(expectedTimeline.getPeriodCount());
|
||||||
|
for (int i = 0; i < actualTimeline.getPeriodCount(); i++) {
|
||||||
|
Period expectedPeriod = new Period();
|
||||||
|
Period actualPeriod = new Period();
|
||||||
|
assertPeriodEqualsExceptIds(
|
||||||
|
expectedTimeline.getPeriod(i, expectedPeriod, /* setIds= */ false),
|
||||||
|
actualTimeline.getPeriod(i, actualPeriod, /* setIds= */ false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that {@link Window windows} are equal except {@link Window#uid} and {@link
|
||||||
|
* Window#manifest}.
|
||||||
|
*/
|
||||||
|
public static void assertWindowEqualsExceptUidAndManifest(
|
||||||
|
Window expectedWindow, Window actualWindow) {
|
||||||
|
Object uid = expectedWindow.uid;
|
||||||
|
@Nullable Object manifest = expectedWindow.manifest;
|
||||||
|
try {
|
||||||
|
expectedWindow.uid = actualWindow.uid;
|
||||||
|
expectedWindow.manifest = actualWindow.manifest;
|
||||||
|
assertThat(actualWindow).isEqualTo(expectedWindow);
|
||||||
|
} finally {
|
||||||
|
expectedWindow.uid = uid;
|
||||||
|
expectedWindow.manifest = manifest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that {@link Period periods} are equal except {@link Period#id} and {@link Period#uid}.
|
||||||
|
*/
|
||||||
|
public static void assertPeriodEqualsExceptIds(Period expectedPeriod, Period actualPeriod) {
|
||||||
|
@Nullable Object id = expectedPeriod.id;
|
||||||
|
@Nullable Object uid = expectedPeriod.uid;
|
||||||
|
try {
|
||||||
|
expectedPeriod.id = actualPeriod.id;
|
||||||
|
expectedPeriod.uid = actualPeriod.uid;
|
||||||
|
assertThat(actualPeriod).isEqualTo(expectedPeriod);
|
||||||
|
} finally {
|
||||||
|
expectedPeriod.id = id;
|
||||||
|
expectedPeriod.uid = uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimelineAsserts() {}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user