diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/ActionFile.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/ActionFile.java index a62fb02ff5..a81d7ffdc2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/ActionFile.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/ActionFile.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.offline; +import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException; import com.google.android.exoplayer2.util.AtomicFile; import com.google.android.exoplayer2.util.Util; import java.io.DataInputStream; @@ -22,12 +23,14 @@ import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; /** * Stores and loads {@link DownloadAction}s to/from a file. */ public final class ActionFile { + private static final String TAG = "ActionFile"; /* package */ static final int VERSION = 0; private final AtomicFile atomicFile; @@ -58,11 +61,15 @@ public final class ActionFile { throw new IOException("Unsupported action file version: " + version); } int actionCount = dataInputStream.readInt(); - DownloadAction[] actions = new DownloadAction[actionCount]; + ArrayList actions = new ArrayList<>(); for (int i = 0; i < actionCount; i++) { - actions[i] = DownloadAction.deserializeFromStream(dataInputStream); + try { + actions.add(DownloadAction.deserializeFromStream(dataInputStream)); + } catch (UnsupportedActionException e) { + // remove DownloadAction is not supported. Ignore the exception and continue loading rest. + } } - return actions; + return actions.toArray(new DownloadAction[0]); } finally { Util.closeQuietly(inputStream); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java index 97a2e3962b..daba0b0d54 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.offline; import android.net.Uri; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -31,9 +30,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -/** Contains the necessary parameters for a download or remove action. */ +/** Contains the necessary parameters for a download action. */ public final class DownloadAction { + /** Thrown when the encoded action data belongs to an unsupported DownloadAction type. */ + public static class UnsupportedActionException extends IOException {} + /** Type for progressive downloads. */ public static final String TYPE_PROGRESSIVE = "progressive"; /** Type for DASH downloads. */ @@ -51,6 +53,8 @@ public final class DownloadAction { * @param data The action data to deserialize. * @return The deserialized action. * @throws IOException If the data could not be deserialized. + * @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction} + * type. Input read position is set to the end of the data. */ public static DownloadAction fromByteArray(byte[] data) throws IOException { ByteArrayInputStream input = new ByteArrayInputStream(data); @@ -67,6 +71,8 @@ public final class DownloadAction { * @return The deserialized action. * @throws IOException If there is an IO error reading from {@code input}, or if the data could * not be deserialized. + * @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction} + * type. Input read position is set to the end of the data. */ public static DownloadAction deserializeFromStream(InputStream input) throws IOException { return readFromStream(new DataInputStream(input)); @@ -108,41 +114,16 @@ public final class DownloadAction { List keys, @Nullable String customCacheKey, @Nullable byte[] data) { - return new DownloadAction( - id, type, uri, /* isRemoveAction= */ false, keys, customCacheKey, data); - } - - /** - * Creates a DASH remove action. - * - * @param type The type of the action. - * @param uri The URI of the media to be removed. - * @param customCacheKey A custom key for cache indexing, or null. - */ - public static DownloadAction createRemoveAction( - String type, Uri uri, @Nullable String customCacheKey) { - return new DownloadAction( - generateId(uri, customCacheKey), - type, - uri, - /* isRemoveAction= */ true, - Collections.emptyList(), - customCacheKey, - /* data= */ null); + return new DownloadAction(id, type, uri, keys, customCacheKey, data); } /** The unique content id. */ public final String id; /** The type of the action. */ public final String type; - /** The uri being downloaded or removed. */ + /** The uri being downloaded. */ public final Uri uri; - /** Whether this is a remove action. If false, this is a download action. */ - public final boolean isRemoveAction; - /** - * Keys of streams to be downloaded. If empty, all streams will be downloaded. Empty if this - * action is a remove action. - */ + /** Keys of streams to be downloaded. If empty, all streams will be downloaded. */ public final List keys; /** A custom key for cache indexing, or null. */ @Nullable public final String customCacheKey; @@ -152,37 +133,26 @@ public final class DownloadAction { /** * @param id The content id. * @param type The type of the action. - * @param uri The uri being downloaded or removed. - * @param isRemoveAction Whether this is a remove action. If false, this is a download action. - * @param keys Keys of streams to be downloaded. If empty, all streams will be downloaded. Empty - * if this action is a remove action. + * @param uri The uri being downloaded. + * @param keys Keys of streams to be downloaded. If empty, all streams will be downloaded. * @param customCacheKey A custom key for cache indexing, or null. - * @param data Custom data for this action. Null if this action is a remove action. + * @param data Custom data for this action. */ private DownloadAction( String id, String type, Uri uri, - boolean isRemoveAction, List keys, @Nullable String customCacheKey, @Nullable byte[] data) { this.id = id; this.type = type; this.uri = uri; - this.isRemoveAction = isRemoveAction; this.customCacheKey = customCacheKey; - if (isRemoveAction) { - Assertions.checkArgument(keys.isEmpty()); - Assertions.checkArgument(data == null); - this.keys = Collections.emptyList(); - this.data = Util.EMPTY_BYTE_ARRAY; - } else { - ArrayList mutableKeys = new ArrayList<>(keys); - Collections.sort(mutableKeys); - this.keys = Collections.unmodifiableList(mutableKeys); - this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY; - } + ArrayList mutableKeys = new ArrayList<>(keys); + Collections.sort(mutableKeys); + this.keys = Collections.unmodifiableList(mutableKeys); + this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY; } /** Serializes itself into a byte array. */ @@ -211,7 +181,6 @@ public final class DownloadAction { return id.equals(that.id) && type.equals(that.type) && uri.equals(that.uri) - && isRemoveAction == that.isRemoveAction && keys.equals(that.keys) && Util.areEqual(customCacheKey, that.customCacheKey) && Arrays.equals(data, that.data); @@ -222,7 +191,6 @@ public final class DownloadAction { int result = type.hashCode(); result = 31 * result + id.hashCode(); result = 31 * result + uri.hashCode(); - result = 31 * result + (isRemoveAction ? 1 : 0); result = 31 * result + keys.hashCode(); result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0); result = 31 * result + Arrays.hashCode(data); @@ -242,7 +210,7 @@ public final class DownloadAction { dataOutputStream.writeUTF(type); dataOutputStream.writeInt(VERSION); dataOutputStream.writeUTF(uri.toString()); - dataOutputStream.writeBoolean(isRemoveAction); + dataOutputStream.writeBoolean(false); dataOutputStream.writeInt(data.length); dataOutputStream.write(data); dataOutputStream.writeInt(keys.size()); @@ -271,10 +239,6 @@ public final class DownloadAction { if (dataLength != 0) { data = new byte[dataLength]; input.readFully(data); - if (isRemoveAction) { - // Remove actions are no longer permitted to have data. - data = null; - } } else { data = null; } @@ -297,8 +261,12 @@ public final class DownloadAction { customCacheKey = input.readBoolean() ? input.readUTF() : null; } + if (isRemoveAction) { + // Remove actions are not supported anymore. + throw new UnsupportedActionException(); + } return new DownloadAction( - generateId(uri, customCacheKey), type, uri, isRemoveAction, keys, customCacheKey, data); + generateId(uri, customCacheKey), type, uri, keys, customCacheKey, data); } private static String generateId(Uri uri, @Nullable String customCacheKey) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java index e6fb10aa21..904a842ad6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java @@ -342,11 +342,11 @@ public final class DownloadManager { } /** - * Handles the given action. + * Adds a download defined by the given action. * - * @param action The action to be executed. + * @param action The download action. */ - public void handleAction(DownloadAction action) { + public void addDownload(DownloadAction action) { Assertions.checkState(!released); dowloadUpdateQueue.add( new DownloadUpdater(action.id) { @@ -392,6 +392,8 @@ public final class DownloadManager { DownloadState onLoad(@Nullable DownloadState downloadState) { if (downloadState != null) { downloadState = downloadState.setRemoveState(); + } else { + logd("Can't remove download. No download with id: " + id); } return downloadState; } @@ -541,15 +543,14 @@ public final class DownloadManager { } private void processDownloadUpdateQueue() { - if (loadingDownload || dowloadUpdateQueue.isEmpty()) { - return; - } - DownloadUpdater downloadUpdater = dowloadUpdateQueue.remove(); - Download download = getDownload(downloadUpdater.id); - if (download != null) { - downloadUpdater.onExisting(download); - } else { - loadDownload(downloadUpdater); + while (!loadingDownload && !dowloadUpdateQueue.isEmpty()) { + DownloadUpdater downloadUpdater = dowloadUpdateQueue.remove(); + Download download = getDownload(downloadUpdater.id); + if (download != null) { + downloadUpdater.onExisting(download); + } else { + loadDownload(downloadUpdater); + } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index 7aad1c6664..b561624725 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -270,11 +270,14 @@ public abstract class DownloadService extends Service { * @param context A {@link Context}. * @param clazz The concrete download service being targeted by the intent. * @param id The content id. + * @param foreground Whether this intent will be used to start the service in the foreground. * @return Created Intent. */ public static Intent buildRemoveDownloadIntent( - Context context, Class clazz, String id) { - return getIntent(context, clazz, ACTION_REMOVE).putExtra(KEY_CONTENT_ID, id); + Context context, Class clazz, String id, boolean foreground) { + return getIntent(context, clazz, ACTION_REMOVE) + .putExtra(KEY_CONTENT_ID, id) + .putExtra(KEY_FOREGROUND, foreground); } /** @@ -308,7 +311,7 @@ public abstract class DownloadService extends Service { */ public static void startWithRemoveDownload( Context context, Class clazz, String id, boolean foreground) { - Intent intent = buildRemoveDownloadIntent(context, clazz, id); + Intent intent = buildRemoveDownloadIntent(context, clazz, id, foreground); if (foreground) { Util.startForegroundService(context, intent); } else { @@ -393,7 +396,7 @@ public abstract class DownloadService extends Service { Log.e(TAG, "Ignored ADD action: Failed to deserialize download_action extra", e); } if (downloadAction != null) { - downloadManager.handleAction(downloadAction); + downloadManager.addDownload(downloadAction); } } break; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java index 5961fb8545..bb8be6b945 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java @@ -166,7 +166,7 @@ public final class DownloadState { action.type, action.uri, action.customCacheKey, - /* state= */ action.isRemoveAction ? STATE_REMOVING : STATE_QUEUED, + /* state= */ STATE_QUEUED, /* downloadPercentage= */ C.PERCENTAGE_UNSET, /* downloadedBytes= */ 0, /* totalBytes= */ C.LENGTH_UNSET, @@ -231,8 +231,7 @@ public final class DownloadState { type, action.uri, action.customCacheKey, - getNextState( - state, manualStopReason != 0 || notMetRequirements != 0, action.isRemoveAction), + getNextState(state, manualStopReason != 0 || notMetRequirements != 0), /* downloadPercentage= */ C.PERCENTAGE_UNSET, downloadedBytes, /* totalBytes= */ C.LENGTH_UNSET, @@ -252,7 +251,7 @@ public final class DownloadState { type, uri, cacheKey, - getNextState(state, manualStopReason != 0 || notMetRequirements != 0, true), + STATE_REMOVING, /* downloadPercentage= */ C.PERCENTAGE_UNSET, downloadedBytes, /* totalBytes= */ C.LENGTH_UNSET, @@ -265,25 +264,19 @@ public final class DownloadState { customMetadata); } - private static int getNextState(int currentState, boolean isStopped, boolean remove) { - int nextState; - if (remove) { - nextState = STATE_REMOVING; + private static int getNextState(int currentState, boolean isStopped) { + if (currentState == STATE_REMOVING || currentState == STATE_RESTARTING) { + return STATE_RESTARTING; + } else if (isStopped) { + return STATE_STOPPED; } else { - if (currentState == STATE_REMOVING || currentState == STATE_RESTARTING) { - nextState = STATE_RESTARTING; - } else if (isStopped) { - nextState = STATE_STOPPED; - } else { - nextState = STATE_QUEUED; - } + return STATE_QUEUED; } - return nextState; } private static StreamKey[] mergeStreamKeys(DownloadState downloadState, DownloadAction action) { StreamKey[] streamKeys = downloadState.streamKeys; - if (!action.isRemoveAction && streamKeys.length > 0) { + if (streamKeys.length > 0) { if (action.keys.isEmpty()) { streamKeys = new StreamKey[0]; } else { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadActionTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadActionTest.java index 571d76125c..eb40d2a1a1 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadActionTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadActionTest.java @@ -20,10 +20,13 @@ import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_HLS; import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_PROGRESSIVE; import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_SS; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import android.net.Uri; +import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -53,92 +56,73 @@ public class DownloadActionTest { data = TestUtil.buildTestData(32); } - @Test - public void testDownloadActionIsNotRemoveAction() { - DownloadAction action = createDownloadAction(uri1); - assertThat(action.isRemoveAction).isFalse(); - } - - @Test - public void testRemoveActionIsRemoveAction() { - DownloadAction action2 = createRemoveAction(uri1); - assertThat(action2.isRemoveAction).isTrue(); - } - @Test public void testSameUri_hasSameId() { - DownloadAction action1 = createDownloadAction(uri1); - DownloadAction action2 = createDownloadAction(uri1); + DownloadAction action1 = createAction(uri1); + DownloadAction action2 = createAction(uri1); assertThat(action1.id.equals(action2.id)).isTrue(); } @Test public void testSameUriDifferentAction_hasSameId() { - DownloadAction action1 = createDownloadAction(uri1); - DownloadAction action2 = createRemoveAction(uri1); + DownloadAction action1 = createAction(uri1); + DownloadAction action2 = createAction(uri1); assertThat(action1.id.equals(action2.id)).isTrue(); } @Test public void testDifferentUri_IsNotSameMedia() { - DownloadAction action1 = createDownloadAction(uri1); - DownloadAction action2 = createDownloadAction(uri2); + DownloadAction action1 = createAction(uri1); + DownloadAction action2 = createAction(uri2); assertThat(action1.id.equals(action2.id)).isFalse(); } @Test public void testSameCacheKeyDifferentUri_hasSameId() { - DownloadAction action1 = DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123"); - DownloadAction action2 = DownloadAction.createRemoveAction(TYPE_DASH, uri2, "key123"); + DownloadAction action1 = createAction(uri1, "key123"); + DownloadAction action2 = createAction(uri2, "key123"); assertThat(action1.id.equals(action2.id)).isTrue(); } @Test public void testDifferentCacheKeyDifferentUri_hasDifferentId() { - DownloadAction action1 = DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123"); - DownloadAction action2 = DownloadAction.createRemoveAction(TYPE_DASH, uri2, "key456"); + DownloadAction action1 = createAction(uri1, "key123"); + DownloadAction action2 = createAction(uri2, "key456"); assertThat(action1.id.equals(action2.id)).isFalse(); } @SuppressWarnings("EqualsWithItself") @Test public void testEquals() { - DownloadAction action1 = createRemoveAction(uri1); + DownloadAction action1 = createAction(uri1); assertThat(action1.equals(action1)).isTrue(); - DownloadAction action2 = createRemoveAction(uri1); - DownloadAction action3 = createRemoveAction(uri1); + DownloadAction action2 = createAction(uri1); + DownloadAction action3 = createAction(uri1); assertEqual(action2, action3); - DownloadAction action4 = createRemoveAction(uri1); - DownloadAction action5 = createDownloadAction(uri1); - assertNotEqual(action4, action5); - - DownloadAction action6 = createDownloadAction(uri1); - DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); + DownloadAction action6 = createAction(uri1); + DownloadAction action7 = createAction(uri1, new StreamKey(0, 0, 0)); assertNotEqual(action6, action7); - DownloadAction action8 = createDownloadAction(uri1, new StreamKey(0, 1, 1)); - DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); + DownloadAction action8 = createAction(uri1, new StreamKey(0, 1, 1)); + DownloadAction action9 = createAction(uri1, new StreamKey(0, 0, 0)); assertNotEqual(action8, action9); - DownloadAction action10 = createRemoveAction(uri1); - DownloadAction action11 = createRemoveAction(uri2); + DownloadAction action10 = createAction(uri1); + DownloadAction action11 = createAction(uri2); assertNotEqual(action10, action11); - DownloadAction action12 = - createDownloadAction(uri1, new StreamKey(0, 0, 0), new StreamKey(0, 1, 1)); - DownloadAction action13 = - createDownloadAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0)); + DownloadAction action12 = createAction(uri1, new StreamKey(0, 0, 0), new StreamKey(0, 1, 1)); + DownloadAction action13 = createAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0)); assertEqual(action12, action13); - DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); - DownloadAction action15 = - createDownloadAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0)); + DownloadAction action14 = createAction(uri1, new StreamKey(0, 0, 0)); + DownloadAction action15 = createAction(uri1, new StreamKey(0, 1, 1), new StreamKey(0, 0, 0)); assertNotEqual(action14, action15); - DownloadAction action16 = createDownloadAction(uri1); - DownloadAction action17 = createDownloadAction(uri1); + DownloadAction action16 = createAction(uri1); + DownloadAction action17 = createAction(uri1); assertEqual(action16, action17); } @@ -151,8 +135,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)), "key123", data)); - assertStreamSerializationRoundTrip( - DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123")); + assertStreamSerializationRoundTrip(createAction(uri1, "key123")); } @Test @@ -164,7 +147,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)), "key123", data)); - assertArraySerializationRoundTrip(DownloadAction.createRemoveAction(TYPE_DASH, uri1, "key123")); + assertArraySerializationRoundTrip(createAction(uri1, "key123")); } @Test @@ -173,9 +156,7 @@ public class DownloadActionTest { "progressive-download-v0", DownloadAction.createDownloadAction( TYPE_PROGRESSIVE, uri1, Collections.emptyList(), "key123", data)); - assertDeserialization( - "progressive-remove-v0", - DownloadAction.createRemoveAction(TYPE_PROGRESSIVE, uri1, "key123")); + assertUnsupportedAction("progressive-remove-v0"); } @Test @@ -188,9 +169,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)), /* customCacheKey= */ null, data)); - assertDeserialization( - "dash-remove-v0", - DownloadAction.createRemoveAction(TYPE_DASH, uri1, /* customCacheKey= */ null)); + assertUnsupportedAction("dash-remove-v0"); } @Test @@ -203,9 +182,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1), new StreamKey(2, 3)), /* customCacheKey= */ null, data)); - assertDeserialization( - "hls-remove-v0", - DownloadAction.createRemoveAction(TYPE_HLS, uri1, /* customCacheKey= */ null)); + assertUnsupportedAction("hls-remove-v0"); } @Test @@ -218,9 +195,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)), /* customCacheKey= */ null, data)); - assertDeserialization( - "hls-remove-v1", - DownloadAction.createRemoveAction(TYPE_HLS, uri1, /* customCacheKey= */ null)); + assertUnsupportedAction("hls-remove-v1"); } @Test @@ -233,9 +208,7 @@ public class DownloadActionTest { toList(new StreamKey(0, 1), new StreamKey(2, 3)), /* customCacheKey= */ null, data)); - assertDeserialization( - "ss-remove-v0", - DownloadAction.createRemoveAction(TYPE_SS, uri1, /* customCacheKey= */ null)); + assertUnsupportedAction("ss-remove-v0"); } @Test @@ -248,18 +221,17 @@ public class DownloadActionTest { toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)), /* customCacheKey= */ null, data)); - assertDeserialization( - "ss-remove-v1", - DownloadAction.createRemoveAction(TYPE_SS, uri1, /* customCacheKey= */ null)); + assertUnsupportedAction("ss-remove-v1"); } - private DownloadAction createDownloadAction(Uri uri, StreamKey... keys) { + private DownloadAction createAction(Uri uri, StreamKey... keys) { return DownloadAction.createDownloadAction( TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, data); } - private DownloadAction createRemoveAction(Uri uri) { - return DownloadAction.createRemoveAction(TYPE_DASH, uri, /* customCacheKey= */ null); + private DownloadAction createAction(Uri uri, @Nullable String customCacheKey) { + return DownloadAction.createDownloadAction( + DownloadAction.TYPE_DASH, uri, Collections.emptyList(), customCacheKey, /* data= */ null); } private static void assertNotEqual(DownloadAction action1, DownloadAction action2) { @@ -298,6 +270,18 @@ public class DownloadActionTest { assertEqual(deserializedAction, expectedAction); } + private static void assertUnsupportedAction(String fileName) throws IOException { + InputStream input = + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), "download-actions/" + fileName); + try { + DownloadAction.deserializeFromStream(input); + fail(); + } catch (UnsupportedActionException e) { + // Expected exception. + } + } + private static List toList(StreamKey... keys) { ArrayList keysList = new ArrayList<>(); Collections.addAll(keysList, keys); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java index 8fd3bebef6..ae4816a80d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java @@ -397,7 +397,6 @@ public class DownloadHelperTest { assertThat(downloadAction.type).isEqualTo(TEST_DOWNLOAD_TYPE); assertThat(downloadAction.uri).isEqualTo(testUri); assertThat(downloadAction.customCacheKey).isEqualTo(TEST_CACHE_KEY); - assertThat(downloadAction.isRemoveAction).isFalse(); assertThat(downloadAction.data).isEqualTo(data); assertThat(downloadAction.keys) .containsExactly( diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java index f22024d87c..2237f5feb2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java @@ -127,16 +127,11 @@ public class DownloadIndexUtilTest { /* customCacheKey= */ "key234", new byte[] {5, 4, 3, 2, 1}); actionFile.store(action1, action2); - DownloadAction action3 = - DownloadAction.createRemoveAction( - TYPE_DASH, Uri.parse("https://www.test.com/download3"), /* customCacheKey= */ "key345"); - actionFile.store(action1, action2, action3); DownloadIndexUtil.upgradeActionFile(actionFile, downloadIndex, /* downloadIdProvider= */ null); assertDownloadIndexContainsAction(action1, DownloadState.STATE_QUEUED); assertDownloadIndexContainsAction(action2, DownloadState.STATE_QUEUED); - assertDownloadIndexContainsAction(action3, DownloadState.STATE_REMOVING); } private void assertDownloadIndexContainsAction(DownloadAction action, int state) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index c6e4867454..4bdd1390c6 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.offline; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; import android.net.Uri; import androidx.test.core.app.ApplicationProvider; @@ -33,7 +32,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.After; @@ -101,21 +99,6 @@ public class DownloadManagerTest { assertThat(exceptionThrown).isTrue(); } - @Test - public void downloadRunner_handleActionReturnsDifferentTaskId_throwsException() { - DownloadRunner runner = new DownloadRunner(uri1).postDownloadAction(); - TaskWrapper task = runner.getTask(); - runner.setTask(new TaskWrapper(task.taskId + 10000)); - boolean exceptionThrown = false; - try { - runner.postDownloadAction(); - // can't put fail() here as it would be caught in the catch below. - } catch (Throwable e) { - exceptionThrown = true; - } - assertThat(exceptionThrown).isTrue(); - } - @Test public void multipleActionsForTheSameContent_executedOnTheSameTask() { // Two download actions on first task @@ -150,11 +133,11 @@ public class DownloadManagerTest { @Test public void postRemoveAction_removes() throws Throwable { DownloadRunner runner = new DownloadRunner(uri1); - TaskWrapper task = runner.postRemoveAction().getTask(); + TaskWrapper task = runner.postDownloadAction().postRemoveAction().getTask(); task.assertRemoving(); - runner.getDownloader(0).unblock().assertReleased().assertStartCount(1); + runner.getDownloader(1).unblock().assertReleased().assertStartCount(1); task.assertRemoved(); - runner.assertCreatedDownloaderCount(1); + runner.assertCreatedDownloaderCount(2); downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -224,29 +207,29 @@ public class DownloadManagerTest { @Test public void downloadNotCancelRemove() throws Throwable { DownloadRunner runner = new DownloadRunner(uri1); - FakeDownloader downloader1 = runner.getDownloader(0); + FakeDownloader downloader1 = runner.getDownloader(1); - runner.postRemoveAction(); + runner.postDownloadAction().postRemoveAction(); downloader1.assertStarted(); runner.postDownloadAction(); downloader1.unblock().assertNotCanceled(); - runner.getDownloader(1).unblock().assertNotCanceled(); + runner.getDownloader(2).unblock().assertNotCanceled(); downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @Test public void secondSameRemoveActionIgnored() throws Throwable { DownloadRunner runner = new DownloadRunner(uri1); - FakeDownloader downloader1 = runner.getDownloader(0); + FakeDownloader downloader1 = runner.getDownloader(1); - runner.postRemoveAction(); + runner.postDownloadAction().postRemoveAction(); downloader1.assertStarted(); runner.postRemoveAction(); downloader1.unblock().assertNotCanceled(); runner.getTask().assertRemoved(); - runner.assertCreatedDownloaderCount(1); + runner.assertCreatedDownloaderCount(2); downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -316,7 +299,7 @@ public class DownloadManagerTest { throws Throwable { setUpDownloadManager(1); DownloadRunner runner1 = new DownloadRunner(uri1).postDownloadAction(); - DownloadRunner runner2 = new DownloadRunner(uri2).postRemoveAction(); + DownloadRunner runner2 = new DownloadRunner(uri2).postDownloadAction().postRemoveAction(); FakeDownloader downloader1 = runner1.getDownloader(0); FakeDownloader downloader2 = runner2.getDownloader(0); @@ -334,7 +317,8 @@ public class DownloadManagerTest { public void downloadActionFollowingRemove_ifMaxDownloadIs1_isNotStarted() throws Throwable { setUpDownloadManager(1); DownloadRunner runner1 = new DownloadRunner(uri1).postDownloadAction(); - DownloadRunner runner2 = new DownloadRunner(uri2).postRemoveAction().postDownloadAction(); + DownloadRunner runner2 = new DownloadRunner(uri2).postDownloadAction().postRemoveAction(); + runner2.postDownloadAction(); FakeDownloader downloader1 = runner1.getDownloader(0); FakeDownloader downloader2 = runner2.getDownloader(0); FakeDownloader downloader3 = runner2.getDownloader(1); @@ -356,7 +340,7 @@ public class DownloadManagerTest { public void getTasks_returnTasks() { TaskWrapper task1 = new DownloadRunner(uri1).postDownloadAction().getTask(); TaskWrapper task2 = new DownloadRunner(uri2).postDownloadAction().getTask(); - TaskWrapper task3 = new DownloadRunner(uri3).postRemoveAction().getTask(); + TaskWrapper task3 = new DownloadRunner(uri3).postDownloadAction().postRemoveAction().getTask(); task3.assertRemoving(); DownloadState[] states = downloadManager.getAllDownloadStates(); @@ -374,7 +358,7 @@ public class DownloadManagerTest { DownloadRunner runner3 = new DownloadRunner(uri3); runner1.postDownloadAction().getTask().assertDownloading(); - runner2.postRemoveAction().getTask().assertRemoving(); + runner2.postDownloadAction().postRemoveAction().getTask().assertRemoving(); runner2.postDownloadAction(); runOnMainThread(() -> downloadManager.stopDownloads()); @@ -382,10 +366,10 @@ public class DownloadManagerTest { runner1.getTask().assertStopped(); // remove actions aren't stopped. - runner2.getDownloader(0).unblock().assertReleased(); + runner2.getDownloader(1).unblock().assertReleased(); runner2.getTask().assertStopped(); // Although remove2 is finished, download2 doesn't start. - runner2.getDownloader(1).assertDoesNotStart(); + runner2.getDownloader(2).assertDoesNotStart(); // When a new remove action is added, it cancels stopped download actions with the same media. runner1.postRemoveAction(); @@ -397,7 +381,7 @@ public class DownloadManagerTest { runOnMainThread(() -> downloadManager.startDownloads()); - runner2.getDownloader(1).assertStarted().unblock(); + runner2.getDownloader(2).assertStarted().unblock(); runner3.getDownloader(0).assertStarted().unblock(); downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); @@ -446,14 +430,14 @@ public class DownloadManagerTest { DownloadRunner runner3 = new DownloadRunner(uri3); runner1.postDownloadAction().getTask().assertDownloading(); - runner2.postRemoveAction().getTask().assertRemoving(); + runner2.postDownloadAction().postRemoveAction().getTask().assertRemoving(); runOnMainThread(() -> downloadManager.stopDownload(runner1.getTask().taskId)); runner1.getTask().assertStopped(); // Other downloads aren't affected. - runner2.getDownloader(0).unblock().assertReleased(); + runner2.getDownloader(1).unblock().assertReleased(); // New download actions can be added and they start. runner3.postDownloadAction().getDownloader(0).assertStarted().unblock(); @@ -501,33 +485,36 @@ public class DownloadManagerTest { private final class DownloadRunner { private final Uri uri; + private final String id; private final ArrayList downloaders; private int createdDownloaderCount = 0; private FakeDownloader downloader; - private TaskWrapper taskWrapper; + private final TaskWrapper taskWrapper; private DownloadRunner(Uri uri) { this.uri = uri; + id = uri.toString(); downloaders = new ArrayList<>(); downloader = addDownloader(); downloaderFactory.registerDownloadRunner(this); + taskWrapper = new TaskWrapper(id); } private DownloadRunner postRemoveAction() { - return postAction(createRemoveAction(uri)); + runOnMainThread(() -> downloadManager.removeDownload(id)); + return this; } private DownloadRunner postDownloadAction(StreamKey... keys) { - return postAction(createDownloadAction(uri, keys)); - } - - private DownloadRunner postAction(DownloadAction action) { - runOnMainThread(() -> downloadManager.handleAction(action)); - if (taskWrapper == null) { - taskWrapper = new TaskWrapper(action.id); - } else { - assertThat(action.id).isEqualTo(taskWrapper.taskId); - } + DownloadAction downloadAction = + DownloadAction.createDownloadAction( + id, + DownloadAction.TYPE_PROGRESSIVE, + uri, + Arrays.asList(keys), + /* customCacheKey= */ null, + /* data= */ null); + runOnMainThread(() -> downloadManager.addDownload(downloadAction)); return this; } @@ -554,10 +541,6 @@ public class DownloadManagerTest { return taskWrapper; } - public void setTask(TaskWrapper taskWrapper) { - this.taskWrapper = taskWrapper; - } - private void assertCreatedDownloaderCount(int count) { assertThat(createdDownloaderCount).isEqualTo(count); } @@ -599,35 +582,8 @@ public class DownloadManagerTest { } private TaskWrapper assertState(@State int expectedState) { - ArrayList receivedStates = new ArrayList<>(); - while (true) { - Integer state = null; - try { - state = downloadManagerListener.pollStateChange(taskId, ASSERT_TRUE_TIMEOUT); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - if (state != null) { - if (expectedState == state) { - return this; - } - receivedStates.add(state); - } else { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < receivedStates.size(); i++) { - if (i > 0) { - sb.append(','); - } - sb.append(DownloadState.getStateString(receivedStates.get(i))); - } - fail( - String.format( - Locale.US, - "expected:<%s> but was:<%s>", - DownloadState.getStateString(expectedState), - sb)); - } - } + downloadManagerListener.assertState(taskId, expectedState, ASSERT_TRUE_TIMEOUT); + return this; } @Override @@ -647,20 +603,6 @@ public class DownloadManagerTest { } } - private static DownloadAction createDownloadAction(Uri uri, StreamKey... keys) { - return DownloadAction.createDownloadAction( - DownloadAction.TYPE_PROGRESSIVE, - uri, - Arrays.asList(keys), - /* customCacheKey= */ null, - /* data= */ null); - } - - private static DownloadAction createRemoveAction(Uri uri) { - return DownloadAction.createRemoveAction( - DownloadAction.TYPE_PROGRESSIVE, uri, /* customCacheKey= */ null); - } - private static final class FakeDownloaderFactory implements DownloaderFactory { private final HashMap downloaders; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java index 5d2af47781..cedbd8a8c4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java @@ -118,20 +118,6 @@ public class DownloadStateTest { assertEqual(mergedDownloadState, expectedDownloadState); } - @Test - public void mergeAction_queuedDownloadRemoveAction_stateBecomesRemoving() { - DownloadAction downloadAction = createRemoveAction(); - DownloadStateBuilder downloadStateBuilder = - new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_QUEUED); - DownloadState downloadState = downloadStateBuilder.build(); - - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); - - DownloadState expectedDownloadState = - downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build(); - assertEqual(mergedDownloadState, expectedDownloadState); - } - @Test public void mergeAction_removingDownloadDownloadAction_stateBecomesRestarting() { DownloadAction downloadAction = createDownloadAction(); @@ -179,22 +165,6 @@ public class DownloadStateTest { assertEqual(mergedDownloadState, downloadState); } - @Test - public void mergeAction_stoppedDownloadRemoveAction_stateBecomesRemoving() { - DownloadAction downloadAction = createRemoveAction(); - DownloadStateBuilder downloadStateBuilder = - new DownloadStateBuilder(downloadAction) - .setState(DownloadState.STATE_STOPPED) - .setManualStopReason(DownloadState.MANUAL_STOP_REASON_UNDEFINED); - DownloadState downloadState = downloadStateBuilder.build(); - - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); - - DownloadState expectedDownloadState = - downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build(); - assertEqual(mergedDownloadState, expectedDownloadState); - } - @Test public void mergeAction_manualStopReasonSetButNotInStoppedState_stateBecomesStopped() { DownloadAction downloadAction = createDownloadAction(); @@ -227,20 +197,6 @@ public class DownloadStateTest { assertEqual(mergedDownloadState, expectedDownloadState); } - @Test - public void mergeAction_restartingDownloadRemoveAction_stateBecomesRemoving() { - DownloadAction downloadAction = createRemoveAction(); - DownloadStateBuilder downloadStateBuilder = - new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_RESTARTING); - DownloadState downloadState = downloadStateBuilder.build(); - - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); - - DownloadState expectedDownloadState = - downloadStateBuilder.setState(DownloadState.STATE_REMOVING).build(); - assertEqual(mergedDownloadState, expectedDownloadState); - } - @Test public void mergeAction_returnsMergedKeys() { StreamKey streamKey1 = new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0); @@ -340,9 +296,4 @@ public class DownloadStateTest { /* customCacheKey= */ null, /* data= */ null); } - - private DownloadAction createRemoveAction() { - return DownloadAction.createRemoveAction( - DownloadAction.TYPE_DASH, testUri, /* customCacheKey= */ null); - } } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java index a215347f15..71f7c9a187 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadTestData.java @@ -22,7 +22,8 @@ import java.nio.charset.Charset; /** Data for DASH downloading tests. */ /* package */ interface DashDownloadTestData { - Uri TEST_MPD_URI = Uri.parse("test.mpd"); + String TEST_ID = "test.mpd"; + Uri TEST_MPD_URI = Uri.parse(TEST_ID); byte[] TEST_MPD = ("\n" diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java index 35dcb77906..1e970e42c3 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.dash.offline; +import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_ID; import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD; import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD_URI; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty; @@ -22,9 +23,7 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import android.net.Uri; import android.os.ConditionVariable; -import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.offline.DefaultDownloadIndex; @@ -230,11 +229,16 @@ public class DownloadManagerDashTest { } private void handleDownloadAction(StreamKey... keys) { - downloadManager.handleAction(newAction(TEST_MPD_URI, false, null, keys)); + ArrayList keysList = new ArrayList<>(); + Collections.addAll(keysList, keys); + DownloadAction action = + DownloadAction.createDownloadAction( + DownloadAction.TYPE_DASH, TEST_MPD_URI, keysList, /* customCacheKey= */ null, null); + downloadManager.addDownload(action); } private void handleRemoveAction() { - downloadManager.handleAction(newAction(TEST_MPD_URI, true, null)); + downloadManager.removeDownload(TEST_ID); } private void createDownloadManager() { @@ -257,20 +261,4 @@ public class DownloadManagerDashTest { }); } - private static DownloadAction newAction( - Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) { - ArrayList keysList = new ArrayList<>(); - Collections.addAll(keysList, keys); - DownloadAction result; - if (isRemoveAction) { - result = - DownloadAction.createRemoveAction( - DownloadAction.TYPE_DASH, uri, /* customCacheKey= */ null); - } else { - result = - DownloadAction.createDownloadAction( - DownloadAction.TYPE_DASH, uri, keysList, /* customCacheKey= */ null, data); - } - return result; - } } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java index 30e511780f..123a02d18a 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.dash.offline; +import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_ID; import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD; import static com.google.android.exoplayer2.source.dash.offline.DashDownloadTestData.TEST_MPD_URI; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty; @@ -22,7 +23,6 @@ import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedDa import android.content.Context; import android.content.Intent; -import android.net.Uri; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -187,37 +187,29 @@ public class DownloadServiceDashTest { assertCacheEmpty(cache); } - private void removeAll() throws Throwable { - callDownloadServiceOnStart(newAction(TEST_MPD_URI, true, null)); - } - - private void downloadKeys(StreamKey... keys) { - callDownloadServiceOnStart(newAction(TEST_MPD_URI, false, null, keys)); - } - - private void callDownloadServiceOnStart(final DownloadAction action) { + private void removeAll() { dummyMainThread.runOnMainThread( () -> { Intent startIntent = - DownloadService.buildAddActionIntent(context, DownloadService.class, action, false); + DownloadService.buildRemoveDownloadIntent( + context, DownloadService.class, TEST_ID, /* foreground= */ false); dashDownloadService.onStartCommand(startIntent, 0, 0); }); } - private static DownloadAction newAction( - Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) { + private void downloadKeys(StreamKey... keys) { ArrayList keysList = new ArrayList<>(); Collections.addAll(keysList, keys); - DownloadAction result; - if (isRemoveAction) { - result = - DownloadAction.createRemoveAction( - DownloadAction.TYPE_DASH, uri, /* customCacheKey= */ null); - } else { - result = - DownloadAction.createDownloadAction( - DownloadAction.TYPE_DASH, uri, keysList, /* customCacheKey= */ null, data); - } - return result; + DownloadAction action = + DownloadAction.createDownloadAction( + DownloadAction.TYPE_DASH, TEST_MPD_URI, keysList, /* customCacheKey= */ null, null); + dummyMainThread.runOnMainThread( + () -> { + Intent startIntent = + DownloadService.buildAddActionIntent( + context, DownloadService.class, action, /* foreground= */ false); + dashDownloadService.onStartCommand(startIntent, 0, 0); + }); } + } diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java index 8216b881f3..86b67e27c7 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java @@ -16,11 +16,15 @@ package com.google.android.exoplayer2.testutil; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import android.os.ConditionVariable; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadState; +import com.google.android.exoplayer2.offline.DownloadState.State; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Locale; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -115,4 +119,37 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen return actionStates.get(taskId); } } + + public void assertState(String taskId, @State int expectedState, int timeoutMs) { + ArrayList receivedStates = new ArrayList<>(); + while (true) { + Integer state = null; + try { + state = pollStateChange(taskId, timeoutMs); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + if (state != null) { + if (expectedState == state) { + return; + } + receivedStates.add(state); + } else { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < receivedStates.size(); i++) { + if (i > 0) { + sb.append(','); + } + sb.append(DownloadState.getStateString(receivedStates.get(i))); + } + fail( + String.format( + Locale.US, + "for download (%s) expected:<%s> but was:<%s>", + taskId, + DownloadState.getStateString(expectedState), + sb)); + } + } + } }