From 93cbae5bff50eca719b0ac2542388f70d11b7e18 Mon Sep 17 00:00:00 2001 From: eguven Date: Wed, 27 Jun 2018 05:48:04 -0700 Subject: [PATCH] Use default Deserializers if non given to DownloadManager Also moved shared code to SegmentDownloadAction between its subclasses. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=202294880 --- RELEASENOTES.md | 1 + .../exoplayer2/demo/DemoApplication.java | 18 +- .../exoplayer2/demo/DownloadTracker.java | 12 +- .../exoplayer2/offline/DownloadAction.java | 57 ++++- .../exoplayer2/offline/DownloadManager.java | 14 +- .../offline/ProgressiveDownloadAction.java | 34 ++- .../offline/ProgressiveDownloadHelper.java | 9 +- .../offline/SegmentDownloadAction.java | 23 +- .../exoplayer2/offline/ActionFileTest.java | 6 +- .../offline/DownloadManagerTest.java | 2 +- .../ProgressiveDownloadActionTest.java | 78 +++---- library/dash/build.gradle | 1 + library/dash/proguard-rules.txt | 7 + .../dash/offline/DashDownloadAction.java | 47 ++-- .../dash/offline/DashDownloadHelper.java | 5 +- library/dash/src/main/proguard-rules.txt | 7 + .../dash/offline/DashDownloadActionTest.java | 112 ++++------ .../dash/offline/DownloadManagerDashTest.java | 11 +- .../dash/offline/DownloadServiceDashTest.java | 13 +- library/hls/build.gradle | 1 + library/hls/proguard-rules.txt | 7 + .../source/hls/offline/HlsDownloadAction.java | 42 +++- .../source/hls/offline/HlsDownloadHelper.java | 7 +- library/hls/src/main/proguard-rules.txt | 7 + .../hls/offline/HlsDownloadActionTest.java | 205 ++++++++++++++++++ library/smoothstreaming/build.gradle | 1 + library/smoothstreaming/proguard-rules.txt | 7 + .../offline/SsDownloadAction.java | 42 +++- .../offline/SsDownloadHelper.java | 5 +- .../src/main/proguard-rules.txt | 7 + .../offline/SsDownloadActionTest.java | 205 ++++++++++++++++++ 31 files changed, 785 insertions(+), 208 deletions(-) create mode 100644 library/dash/proguard-rules.txt create mode 100644 library/dash/src/main/proguard-rules.txt create mode 100644 library/hls/proguard-rules.txt create mode 100644 library/hls/src/main/proguard-rules.txt create mode 100644 library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadActionTest.java create mode 100644 library/smoothstreaming/proguard-rules.txt create mode 100644 library/smoothstreaming/src/main/proguard-rules.txt create mode 100644 library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadActionTest.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 56c7404649..88e8a22768 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -37,6 +37,7 @@ CommentFrame to InternalFrame for frames with gapless metadata in MP4. * Allow setting the `Looper`, which is used to access the player, in `ExoPlayerFactory` ([#4278](https://github.com/google/ExoPlayer/issues/4278)). +* Use default Deserializers if non given to DownloadManager. * Deprecate `Player.DefaultEventListener` as selective listener overrides can be directly made with the `Player.EventListener` interface. * Deprecate `DefaultAnalyticsListener` as selective listener overrides can be diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java index b5c127d2e3..12707817db 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java @@ -16,13 +16,8 @@ package com.google.android.exoplayer2.demo; import android.app.Application; -import com.google.android.exoplayer2.offline.DownloadAction.Deserializer; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; -import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; -import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; -import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; -import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; @@ -46,13 +41,6 @@ public class DemoApplication extends Application { private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions"; private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads"; private static final int MAX_SIMULTANEOUS_DOWNLOADS = 2; - private static final Deserializer[] DOWNLOAD_DESERIALIZERS = - new Deserializer[] { - DashDownloadAction.DESERIALIZER, - HlsDownloadAction.DESERIALIZER, - SsDownloadAction.DESERIALIZER, - ProgressiveDownloadAction.DESERIALIZER - }; protected String userAgent; @@ -105,14 +93,12 @@ public class DemoApplication extends Application { downloaderConstructorHelper, MAX_SIMULTANEOUS_DOWNLOADS, DownloadManager.DEFAULT_MIN_RETRY_COUNT, - new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE), - DOWNLOAD_DESERIALIZERS); + new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE)); downloadTracker = new DownloadTracker( /* context= */ this, buildDataSourceFactory(/* listener= */ null), - new File(getDownloadDirectory(), DOWNLOAD_TRACKER_ACTION_FILE), - DOWNLOAD_DESERIALIZERS); + new File(getDownloadDirectory(), DOWNLOAD_TRACKER_ACTION_FILE)); downloadManager.addListener(downloadTracker); } } diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java index 14dff84ab5..f20e41d8f7 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java @@ -36,7 +36,6 @@ import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.TaskState; import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper; -import com.google.android.exoplayer2.offline.SegmentDownloadAction; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.offline.TrackKey; import com.google.android.exoplayer2.source.TrackGroup; @@ -86,7 +85,7 @@ public class DownloadTracker implements DownloadManager.Listener { Context context, DataSource.Factory dataSourceFactory, File actionFile, - DownloadAction.Deserializer[] deserializers) { + DownloadAction.Deserializer... deserializers) { this.context = context.getApplicationContext(); this.dataSourceFactory = dataSourceFactory; this.actionFile = new ActionFile(actionFile); @@ -96,7 +95,8 @@ public class DownloadTracker implements DownloadManager.Listener { HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker"); actionFileWriteThread.start(); actionFileWriteHandler = new Handler(actionFileWriteThread.getLooper()); - loadTrackedActions(deserializers); + loadTrackedActions( + deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers()); } public void addListener(Listener listener) { @@ -116,11 +116,7 @@ public class DownloadTracker implements DownloadManager.Listener { if (!trackedDownloadStates.containsKey(uri)) { return Collections.emptyList(); } - DownloadAction action = trackedDownloadStates.get(uri); - if (action instanceof SegmentDownloadAction) { - return ((SegmentDownloadAction) action).keys; - } - return Collections.emptyList(); + return trackedDownloadStates.get(uri).getKeys(); } public void toggleDownload(Activity activity, String name, Uri uri, String extension) { 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 98360b909c..20b7860784 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,6 +17,7 @@ package com.google.android.exoplayer2.offline; import android.net.Uri; import android.support.annotation.Nullable; +import com.google.android.exoplayer2.util.Assertions; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -24,6 +25,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** Contains the necessary parameters for a download or remove action. */ public abstract class DownloadAction { @@ -50,6 +53,48 @@ public abstract class DownloadAction { throws IOException; } + private static @Nullable Deserializer[] defaultDeserializers; + + /** Returns available default {@link Deserializer}s. */ + public static synchronized Deserializer[] getDefaultDeserializers() { + if (defaultDeserializers != null) { + return defaultDeserializers; + } + Deserializer[] deserializers = new Deserializer[4]; + int count = 0; + deserializers[count++] = ProgressiveDownloadAction.DESERIALIZER; + Class clazz; + // Full class names used for constructor args so the LINT rule triggers if any of them move. + try { + // LINT.IfChange + clazz = Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloadAction"); + // LINT.ThenChange(../../../../../../../../../dash/proguard-rules.txt) + deserializers[count++] = getDeserializer(clazz); + } catch (Exception e) { + // Do nothing. + } + try { + // LINT.IfChange + clazz = Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction"); + // LINT.ThenChange(../../../../../../../../../hls/proguard-rules.txt) + deserializers[count++] = getDeserializer(clazz); + } catch (Exception e) { + // Do nothing. + } + try { + // LINT.IfChange + clazz = + Class.forName( + "com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction"); + // LINT.ThenChange(../../../../../../../../../smoothstreaming/proguard-rules.txt) + deserializers[count++] = getDeserializer(clazz); + } catch (Exception e) { + // Do nothing. + } + defaultDeserializers = Arrays.copyOf(Assertions.checkNotNull(deserializers), count); + return defaultDeserializers; + } + /** * Deserializes one action that was serialized with {@link #serializeToStream(DownloadAction, * OutputStream)} from the {@code input}, using the {@link Deserializer}s that supports the @@ -132,11 +177,16 @@ public abstract class DownloadAction { return uri.equals(other.uri); } + /** Returns keys of tracks to be downloaded. */ + public List getKeys() { + return Collections.emptyList(); + } + /** Serializes itself into the {@code output}. */ protected abstract void writeToStream(DataOutputStream output) throws IOException; /** Creates a {@link Downloader} with the given parameters. */ - protected abstract Downloader createDownloader( + public abstract Downloader createDownloader( DownloaderConstructorHelper downloaderConstructorHelper); @Override @@ -160,4 +210,9 @@ public abstract class DownloadAction { return result; } + private static Deserializer getDeserializer(Class clazz) + throws NoSuchFieldException, IllegalAccessException { + Object value = clazz.getDeclaredField("DESERIALIZER").get(null); + return (Deserializer) Assertions.checkNotNull(value); + } } 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 4636552a77..dcfb8a8cbe 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 @@ -108,7 +108,8 @@ public final class DownloadManager { * @param upstreamDataSourceFactory A {@link DataSource.Factory} for creating data sources for * downloading upstream data. * @param actionSaveFile File to save active actions. - * @param deserializers Used to deserialize {@link DownloadAction}s. + * @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link + * DownloadAction#getDefaultDeserializers()} is used instead. */ public DownloadManager( Cache cache, @@ -127,7 +128,8 @@ public final class DownloadManager { * @param constructorHelper A {@link DownloaderConstructorHelper} to create {@link Downloader}s * for downloading data. * @param actionFile The file in which active actions are saved. - * @param deserializers Used to deserialize {@link DownloadAction}s. + * @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link + * DownloadAction#getDefaultDeserializers()} is used instead. */ public DownloadManager( DownloaderConstructorHelper constructorHelper, @@ -149,7 +151,8 @@ public final class DownloadManager { * @param maxSimultaneousDownloads The maximum number of simultaneous download tasks. * @param minRetryCount The minimum number of times a task must be retried before failing. * @param actionFile The file in which active actions are saved. - * @param deserializers Used to deserialize {@link DownloadAction}s. + * @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link + * DownloadAction#getDefaultDeserializers()} is used instead. */ public DownloadManager( DownloaderConstructorHelper constructorHelper, @@ -157,13 +160,12 @@ public final class DownloadManager { int minRetryCount, File actionFile, Deserializer... deserializers) { - Assertions.checkArgument(deserializers.length > 0, "At least one Deserializer is required."); - this.downloaderConstructorHelper = constructorHelper; this.maxActiveDownloadTasks = maxSimultaneousDownloads; this.minRetryCount = minRetryCount; this.actionFile = new ActionFile(actionFile); - this.deserializers = deserializers; + this.deserializers = + deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers(); this.downloadsStopped = true; tasks = new ArrayList<>(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadAction.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadAction.java index d8db6f96c2..7ced2fa41b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadAction.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadAction.java @@ -44,7 +44,33 @@ public final class ProgressiveDownloadAction extends DownloadAction { } }; - public final @Nullable String customCacheKey; + private final @Nullable String customCacheKey; + + /** + * Creates a progressive stream download action. + * + * @param uri Uri of the data to be downloaded. + * @param data Optional custom data for this action. + * @param customCacheKey A custom key that uniquely identifies the original stream. If not null it + * is used for cache indexing. + */ + public static ProgressiveDownloadAction createDownloadAction( + Uri uri, @Nullable byte[] data, @Nullable String customCacheKey) { + return new ProgressiveDownloadAction(uri, /* isRemoveAction= */ false, data, customCacheKey); + } + + /** + * Creates a progressive stream remove action. + * + * @param uri Uri of the data to be removed. + * @param data Optional custom data for this action. + * @param customCacheKey A custom key that uniquely identifies the original stream. If not null it + * is used for cache indexing. + */ + public static ProgressiveDownloadAction createRemoveAction( + Uri uri, @Nullable byte[] data, @Nullable String customCacheKey) { + return new ProgressiveDownloadAction(uri, /* isRemoveAction= */ true, data, customCacheKey); + } /** * @param uri Uri of the data to be downloaded. @@ -52,7 +78,10 @@ public final class ProgressiveDownloadAction extends DownloadAction { * @param data Optional custom data for this action. * @param customCacheKey A custom key that uniquely identifies the original stream. If not null it * is used for cache indexing. + * @deprecated Use {@link #createDownloadAction(Uri, byte[], String)} or {@link + * #createRemoveAction(Uri, byte[], String)}. */ + @Deprecated public ProgressiveDownloadAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, @Nullable String customCacheKey) { super(TYPE, VERSION, uri, isRemoveAction, data); @@ -60,7 +89,7 @@ public final class ProgressiveDownloadAction extends DownloadAction { } @Override - protected ProgressiveDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { + public ProgressiveDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { return new ProgressiveDownloader(uri, customCacheKey, constructorHelper); } @@ -105,4 +134,5 @@ public final class ProgressiveDownloadAction extends DownloadAction { private String getCacheKey() { return customCacheKey != null ? customCacheKey : CacheUtil.generateKey(uri); } + } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadHelper.java index 49b7e36ea6..473209803a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadHelper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloadHelper.java @@ -51,12 +51,13 @@ public final class ProgressiveDownloadHelper extends DownloadHelper { } @Override - public DownloadAction getDownloadAction(@Nullable byte[] data, List trackKeys) { - return new ProgressiveDownloadAction(uri, false, data, customCacheKey); + public ProgressiveDownloadAction getDownloadAction( + @Nullable byte[] data, List trackKeys) { + return ProgressiveDownloadAction.createDownloadAction(uri, data, customCacheKey); } @Override - public DownloadAction getRemoveAction(@Nullable byte[] data) { - return new ProgressiveDownloadAction(uri, true, data, customCacheKey); + public ProgressiveDownloadAction getRemoveAction(@Nullable byte[] data) { + return ProgressiveDownloadAction.createRemoveAction(uri, data, customCacheKey); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloadAction.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloadAction.java index 4ea5d6aa39..403b4e797b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloadAction.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloadAction.java @@ -46,13 +46,18 @@ public abstract class SegmentDownloadAction extends DownloadAction { int keyCount = input.readInt(); List keys = new ArrayList<>(); for (int i = 0; i < keyCount; i++) { - keys.add(readKey(input)); + keys.add(readKey(version, input)); } return createDownloadAction(uri, isRemoveAction, data, keys); } /** Deserializes a key from the {@code input}. */ - protected abstract StreamKey readKey(DataInputStream input) throws IOException; + protected StreamKey readKey(int version, DataInputStream input) throws IOException { + int periodIndex = input.readInt(); + int groupIndex = input.readInt(); + int trackIndex = input.readInt(); + return new StreamKey(periodIndex, groupIndex, trackIndex); + } /** Returns a {@link DownloadAction}. */ protected abstract DownloadAction createDownloadAction( @@ -88,6 +93,11 @@ public abstract class SegmentDownloadAction extends DownloadAction { } } + @Override + public List getKeys() { + return keys; + } + @Override public final void writeToStream(DataOutputStream output) throws IOException { output.writeUTF(uri.toString()); @@ -100,9 +110,6 @@ public abstract class SegmentDownloadAction extends DownloadAction { } } - /** Serializes the {@code key} into the {@code output}. */ - protected abstract void writeKey(DataOutputStream output, StreamKey key) throws IOException; - @Override public boolean equals(@Nullable Object o) { if (this == o) { @@ -122,4 +129,10 @@ public abstract class SegmentDownloadAction extends DownloadAction { return result; } + /** Serializes the {@code key} into the {@code output}. */ + private void writeKey(DataOutputStream output, StreamKey key) throws IOException { + output.writeInt(key.periodIndex); + output.writeInt(key.groupIndex); + output.writeInt(key.trackIndex); + } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/ActionFileTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/ActionFileTest.java index e821bc34a0..634d541d39 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/ActionFileTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/ActionFileTest.java @@ -33,9 +33,7 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -/** - * Unit tests for {@link ProgressiveDownloadAction}. - */ +/** Unit tests for {@link ActionFile}. */ @RunWith(RobolectricTestRunner.class) public class ActionFileTest { @@ -258,7 +256,7 @@ public class ActionFileTest { } @Override - protected Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) { + public Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) { return null; } 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 0d0bf73d04..4a1876f69c 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 @@ -510,7 +510,7 @@ public class DownloadManagerTest { } @Override - protected Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) { + public Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) { return downloader; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/ProgressiveDownloadActionTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/ProgressiveDownloadActionTest.java index bc3732e3d3..df5e7dd044 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/ProgressiveDownloadActionTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/ProgressiveDownloadActionTest.java @@ -32,9 +32,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link ProgressiveDownloadAction}. - */ +/** Unit tests for {@link ProgressiveDownloadAction}. */ @RunWith(RobolectricTestRunner.class) public class ProgressiveDownloadActionTest { @@ -49,112 +47,109 @@ public class ProgressiveDownloadActionTest { @Test public void testDownloadActionIsNotRemoveAction() throws Exception { - ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action = createDownloadAction(uri1, null); assertThat(action.isRemoveAction).isFalse(); } @Test public void testRemoveActionisRemoveAction() throws Exception { - ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, true, null, null); + DownloadAction action2 = createRemoveAction(uri1, null); assertThat(action2.isRemoveAction).isTrue(); } @Test public void testCreateDownloader() throws Exception { MockitoAnnotations.initMocks(this); - ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null); - DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper( - Mockito.mock(Cache.class), DummyDataSource.FACTORY); + DownloadAction action = createDownloadAction(uri1, null); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY); assertThat(action.createDownloader(constructorHelper)).isNotNull(); } @Test public void testSameUriCacheKeyDifferentAction_IsSameMedia() throws Exception { - ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, null); - ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action1 = createRemoveAction(uri1, null); + DownloadAction action2 = createDownloadAction(uri1, null); assertSameMedia(action1, action2); } @Test public void testNullCacheKeyDifferentUriAction_IsNotSameMedia() throws Exception { - ProgressiveDownloadAction action3 = new ProgressiveDownloadAction(uri2, true, null, null); - ProgressiveDownloadAction action4 = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action3 = createRemoveAction(uri2, null); + DownloadAction action4 = createDownloadAction(uri1, null); assertNotSameMedia(action3, action4); } @Test public void testSameCacheKeyDifferentUriAction_IsSameMedia() throws Exception { - ProgressiveDownloadAction action5 = new ProgressiveDownloadAction(uri2, true, null, "key"); - ProgressiveDownloadAction action6 = new ProgressiveDownloadAction(uri1, false, null, "key"); + DownloadAction action5 = createRemoveAction(uri2, "key"); + DownloadAction action6 = createDownloadAction(uri1, "key"); assertSameMedia(action5, action6); } @Test public void testSameUriDifferentCacheKeyAction_IsNotSameMedia() throws Exception { - ProgressiveDownloadAction action7 = new ProgressiveDownloadAction(uri1, true, null, "key"); - ProgressiveDownloadAction action8 = new ProgressiveDownloadAction(uri1, false, null, "key2"); + DownloadAction action7 = createRemoveAction(uri1, "key"); + DownloadAction action8 = createDownloadAction(uri1, "key2"); assertNotSameMedia(action7, action8); } @Test public void testSameUriNullCacheKeyAction_IsNotSameMedia() throws Exception { - ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, "key"); - ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action1 = createRemoveAction(uri1, "key"); + DownloadAction action2 = createDownloadAction(uri1, null); assertNotSameMedia(action1, action2); } @Test public void testEquals() throws Exception { - ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, null); + DownloadAction action1 = createRemoveAction(uri1, null); assertThat(action1.equals(action1)).isTrue(); - ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, true, null, null); - ProgressiveDownloadAction action3 = new ProgressiveDownloadAction(uri1, true, null, null); + DownloadAction action2 = createRemoveAction(uri1, null); + DownloadAction action3 = createRemoveAction(uri1, null); assertThat(action2.equals(action3)).isTrue(); - ProgressiveDownloadAction action4 = new ProgressiveDownloadAction(uri1, true, null, null); - ProgressiveDownloadAction action5 = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action4 = createRemoveAction(uri1, null); + DownloadAction action5 = createDownloadAction(uri1, null); assertThat(action4.equals(action5)).isFalse(); - ProgressiveDownloadAction action6 = new ProgressiveDownloadAction(uri1, true, null, null); - ProgressiveDownloadAction action7 = new ProgressiveDownloadAction(uri1, true, null, "key"); + DownloadAction action6 = createRemoveAction(uri1, null); + DownloadAction action7 = createRemoveAction(uri1, "key"); assertThat(action6.equals(action7)).isFalse(); - ProgressiveDownloadAction action8 = new ProgressiveDownloadAction(uri1, true, null, "key2"); - ProgressiveDownloadAction action9 = new ProgressiveDownloadAction(uri1, true, null, "key"); + DownloadAction action8 = createRemoveAction(uri1, "key2"); + DownloadAction action9 = createRemoveAction(uri1, "key"); assertThat(action8.equals(action9)).isFalse(); - ProgressiveDownloadAction action10 = new ProgressiveDownloadAction(uri1, true, null, null); - ProgressiveDownloadAction action11 = new ProgressiveDownloadAction(uri2, true, null, null); + DownloadAction action10 = createRemoveAction(uri1, null); + DownloadAction action11 = createRemoveAction(uri2, null); assertThat(action10.equals(action11)).isFalse(); } @Test public void testSerializerGetType() throws Exception { - ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null); + DownloadAction action = createDownloadAction(uri1, null); assertThat(action.type).isNotNull(); } @Test public void testSerializerWriteRead() throws Exception { - doTestSerializationRoundTrip(new ProgressiveDownloadAction(uri1, false, null, null)); - doTestSerializationRoundTrip(new ProgressiveDownloadAction(uri2, true, null, "key")); + doTestSerializationRoundTrip(createDownloadAction(uri1, null)); + doTestSerializationRoundTrip(createRemoveAction(uri2, "key")); } - private void assertSameMedia( - ProgressiveDownloadAction action1, ProgressiveDownloadAction action2) { + private void assertSameMedia(DownloadAction action1, DownloadAction action2) { assertThat(action1.isSameMedia(action2)).isTrue(); assertThat(action2.isSameMedia(action1)).isTrue(); } - private void assertNotSameMedia( - ProgressiveDownloadAction action1, ProgressiveDownloadAction action2) { + private void assertNotSameMedia(DownloadAction action1, DownloadAction action2) { assertThat(action1.isSameMedia(action2)).isFalse(); assertThat(action2.isSameMedia(action1)).isFalse(); } - private static void doTestSerializationRoundTrip(ProgressiveDownloadAction action) - throws IOException { + private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream output = new DataOutputStream(out); DownloadAction.serializeToStream(action, output); @@ -168,4 +163,11 @@ public class ProgressiveDownloadActionTest { assertThat(action2).isEqualTo(action); } + private static DownloadAction createDownloadAction(Uri uri1, String customCacheKey) { + return ProgressiveDownloadAction.createDownloadAction(uri1, null, customCacheKey); + } + + private static DownloadAction createRemoveAction(Uri uri1, String customCacheKey) { + return ProgressiveDownloadAction.createRemoveAction(uri1, null, customCacheKey); + } } diff --git a/library/dash/build.gradle b/library/dash/build.gradle index fa8fe34935..40b014aaf9 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -26,6 +26,7 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' } buildTypes { diff --git a/library/dash/proguard-rules.txt b/library/dash/proguard-rules.txt new file mode 100644 index 0000000000..f8725fff4d --- /dev/null +++ b/library/dash/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the dash module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.dash.offline.DashDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.dash.offline.DashDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadAction.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadAction.java index ebaf51ef05..f36a018e5b 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadAction.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadAction.java @@ -21,9 +21,7 @@ import com.google.android.exoplayer2.offline.DownloadAction; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.SegmentDownloadAction; import com.google.android.exoplayer2.offline.StreamKey; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +import java.util.Collections; import java.util.List; /** An action to download or remove downloaded DASH streams. */ @@ -34,15 +32,6 @@ public final class DashDownloadAction extends SegmentDownloadAction { public static final Deserializer DESERIALIZER = new SegmentDownloadActionDeserializer(TYPE, VERSION) { - - @Override - protected StreamKey readKey(DataInputStream input) throws IOException { - int periodIndex = input.readInt(); - int groupIndex = input.readInt(); - int trackIndex = input.readInt(); - return new StreamKey(periodIndex, groupIndex, trackIndex); - } - @Override protected DownloadAction createDownloadAction( Uri uri, boolean isRemoveAction, byte[] data, List keys) { @@ -50,28 +39,46 @@ public final class DashDownloadAction extends SegmentDownloadAction { } }; + /** + * Creates a DASH download action. + * + * @param uri The URI of the media to be downloaded. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + * @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded. + */ + public static DashDownloadAction createDownloadAction( + Uri uri, @Nullable byte[] data, List keys) { + return new DashDownloadAction(uri, /* isRemoveAction= */ false, data, keys); + } + + /** + * Creates a DASH remove action. + * + * @param uri The URI of the media to be removed. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + */ + public static DashDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) { + return new DashDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + } + /** * @param uri The DASH manifest URI. * @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded. * @param data Optional custom data for this action. * @param keys Keys of representations to be downloaded. If empty, all representations are * downloaded. If {@code removeAction} is true, {@code keys} must be empty. + * @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link + * #createRemoveAction(Uri, byte[])}. */ + @Deprecated public DashDownloadAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, List keys) { super(TYPE, VERSION, uri, isRemoveAction, data, keys); } @Override - protected DashDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { + public DashDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { return new DashDownloader(uri, keys, constructorHelper); } - @Override - protected void writeKey(DataOutputStream output, StreamKey key) throws IOException { - output.writeInt(key.periodIndex); - output.writeInt(key.groupIndex); - output.writeInt(key.trackIndex); - } - } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadHelper.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadHelper.java index 8f12377de7..91e41b9ded 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadHelper.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadHelper.java @@ -33,7 +33,6 @@ import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -88,12 +87,12 @@ public final class DashDownloadHelper extends DownloadHelper { @Override public DashDownloadAction getDownloadAction(@Nullable byte[] data, List trackKeys) { - return new DashDownloadAction(uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys)); + return DashDownloadAction.createDownloadAction(uri, data, toStreamKeys(trackKeys)); } @Override public DashDownloadAction getRemoveAction(@Nullable byte[] data) { - return new DashDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + return DashDownloadAction.createRemoveAction(uri, data); } private static List toStreamKeys(List trackKeys) { diff --git a/library/dash/src/main/proguard-rules.txt b/library/dash/src/main/proguard-rules.txt new file mode 100644 index 0000000000..f8725fff4d --- /dev/null +++ b/library/dash/src/main/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the dash module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.dash.offline.DashDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.dash.offline.DashDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java index 3b32dacea3..0ebf6bb628 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloadActionTest.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source.dash.offline; import static com.google.common.truth.Truth.assertThat; import android.net.Uri; -import android.support.annotation.Nullable; import com.google.android.exoplayer2.offline.DownloadAction; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.StreamKey; @@ -38,9 +37,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -/** - * Unit tests for {@link DashDownloadAction}. - */ +/** Unit tests for {@link DashDownloadAction}. */ @RunWith(RobolectricTestRunner.class) public class DashDownloadActionTest { @@ -55,130 +52,106 @@ public class DashDownloadActionTest { @Test public void testDownloadActionIsNotRemoveAction() { - DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action = createDownloadAction(uri1); assertThat(action.isRemoveAction).isFalse(); } @Test - public void testRemoveActionisRemoveAction() { - DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); + public void testRemoveActionIsRemoveAction() { + DownloadAction action2 = createRemoveAction(uri1); assertThat(action2.isRemoveAction).isTrue(); } @Test public void testCreateDownloader() { MockitoAnnotations.initMocks(this); - DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); - DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper( - Mockito.mock(Cache.class), DummyDataSource.FACTORY); + DownloadAction action = createDownloadAction(uri1); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY); assertThat(action.createDownloader(constructorHelper)).isNotNull(); } @Test public void testSameUriDifferentAction_IsSameMedia() { - DashDownloadAction action1 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); - DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action1 = createRemoveAction(uri1); + DownloadAction action2 = createDownloadAction(uri1); assertThat(action1.isSameMedia(action2)).isTrue(); } @Test public void testDifferentUriAndAction_IsNotSameMedia() { - DashDownloadAction action3 = newAction(uri2, /* isRemoveAction= */ true, /* data= */ null); - DashDownloadAction action4 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action3 = createRemoveAction(uri2); + DownloadAction action4 = createDownloadAction(uri1); assertThat(action3.isSameMedia(action4)).isFalse(); } @SuppressWarnings("EqualsWithItself") @Test public void testEquals() { - DashDownloadAction action1 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); + DownloadAction action1 = createRemoveAction(uri1); assertThat(action1.equals(action1)).isTrue(); - DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); - DashDownloadAction action3 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); + DownloadAction action2 = createRemoveAction(uri1); + DownloadAction action3 = createRemoveAction(uri1); assertEqual(action2, action3); - DashDownloadAction action4 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); - DashDownloadAction action5 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action4 = createRemoveAction(uri1); + DownloadAction action5 = createDownloadAction(uri1); assertNotEqual(action4, action5); - DashDownloadAction action6 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); - DashDownloadAction action7 = - newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0)); + DownloadAction action6 = createDownloadAction(uri1); + DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); assertNotEqual(action6, action7); - DashDownloadAction action8 = - newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(1, 1, 1)); - DashDownloadAction action9 = - newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0)); + DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1, 1)); + DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); assertNotEqual(action8, action9); - DashDownloadAction action10 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null); - DashDownloadAction action11 = newAction(uri2, /* isRemoveAction= */ true, /* data= */ null); + DownloadAction action10 = createRemoveAction(uri1); + DownloadAction action11 = createRemoveAction(uri2); assertNotEqual(action10, action11); - DashDownloadAction action12 = - newAction( - uri1, - /* isRemoveAction= */ false, - /* data= */ null, - new StreamKey(0, 0, 0), - new StreamKey(1, 1, 1)); - DashDownloadAction action13 = - newAction( - uri1, - /* isRemoveAction= */ false, - /* data= */ null, - new StreamKey(1, 1, 1), - new StreamKey(0, 0, 0)); + DownloadAction action12 = + createDownloadAction(uri1, new StreamKey(0, 0, 0), new StreamKey(1, 1, 1)); + DownloadAction action13 = + createDownloadAction(uri1, new StreamKey(1, 1, 1), new StreamKey(0, 0, 0)); assertEqual(action12, action13); - DashDownloadAction action14 = - newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0)); - DashDownloadAction action15 = - newAction( - uri1, - /* isRemoveAction= */ false, - /* data= */ null, - new StreamKey(1, 1, 1), - new StreamKey(0, 0, 0)); + DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0, 0)); + DownloadAction action15 = + createDownloadAction(uri1, new StreamKey(1, 1, 1), new StreamKey(0, 0, 0)); assertNotEqual(action14, action15); - DashDownloadAction action16 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); - DashDownloadAction action17 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action16 = createDownloadAction(uri1); + DownloadAction action17 = createDownloadAction(uri1); assertEqual(action16, action17); } @Test public void testSerializerGetType() { - DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null); + DownloadAction action = createDownloadAction(uri1); assertThat(action.type).isNotNull(); } @Test public void testSerializerWriteRead() throws Exception { - doTestSerializationRoundTrip(newAction(uri1, /* isRemoveAction= */ false, /* data= */ null)); - doTestSerializationRoundTrip(newAction(uri1, /* isRemoveAction= */ true, /* data= */ null)); + doTestSerializationRoundTrip(createDownloadAction(uri1)); + doTestSerializationRoundTrip(createRemoveAction(uri1)); doTestSerializationRoundTrip( - newAction( - uri2, - /* isRemoveAction= */ false, - /* data= */ null, - new StreamKey(0, 0, 0), - new StreamKey(1, 1, 1))); + createDownloadAction(uri2, new StreamKey(0, 0, 0), new StreamKey(1, 1, 1))); } - private static void assertNotEqual(DashDownloadAction action1, DashDownloadAction action2) { + private static void assertNotEqual(DownloadAction action1, DownloadAction action2) { assertThat(action1).isNotEqualTo(action2); assertThat(action2).isNotEqualTo(action1); } - private static void assertEqual(DashDownloadAction action1, DashDownloadAction action2) { + private static void assertEqual(DownloadAction action1, DownloadAction action2) { assertThat(action1).isEqualTo(action2); assertThat(action2).isEqualTo(action1); } - private static void doTestSerializationRoundTrip(DashDownloadAction action) throws IOException { + private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream output = new DataOutputStream(out); DownloadAction.serializeToStream(action, output); @@ -192,10 +165,13 @@ public class DashDownloadActionTest { assertThat(action).isEqualTo(action2); } - private static DashDownloadAction newAction( - Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) { + private static DownloadAction createDownloadAction(Uri uri, StreamKey... keys) { ArrayList keysList = new ArrayList<>(); Collections.addAll(keysList, keys); - return new DashDownloadAction(uri, isRemoveAction, data, keysList); + return DashDownloadAction.createDownloadAction(uri, null, keysList); + } + + private static DownloadAction createRemoveAction(Uri uri) { + return DashDownloadAction.createRemoveAction(uri, null); } } 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 e14e12d12a..d2ba826c66 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 @@ -25,6 +25,7 @@ import android.content.Context; import android.net.Uri; import android.os.ConditionVariable; import android.support.annotation.Nullable; +import com.google.android.exoplayer2.offline.DownloadAction; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.StreamKey; @@ -279,10 +280,16 @@ public class DownloadManagerDashTest { }); } - private static DashDownloadAction newAction( + private static DownloadAction newAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) { ArrayList keysList = new ArrayList<>(); Collections.addAll(keysList, keys); - return new DashDownloadAction(uri, isRemoveAction, data, keysList); + DownloadAction result; + if (isRemoveAction) { + result = DashDownloadAction.createRemoveAction(uri, data); + } else { + result = DashDownloadAction.createDownloadAction(uri, data, keysList); + } + 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 ce9ba34a18..4553eaf5c4 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 @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.Nullable; +import com.google.android.exoplayer2.offline.DownloadAction; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.TaskState; import com.google.android.exoplayer2.offline.DownloadService; @@ -216,7 +217,7 @@ public class DownloadServiceDashTest { callDownloadServiceOnStart(newAction(TEST_MPD_URI, false, null, keys)); } - private void callDownloadServiceOnStart(final DashDownloadAction action) { + private void callDownloadServiceOnStart(final DownloadAction action) { dummyMainThread.runOnMainThread( new Runnable() { @Override @@ -228,10 +229,16 @@ public class DownloadServiceDashTest { }); } - private static DashDownloadAction newAction( + private static DownloadAction newAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) { ArrayList keysList = new ArrayList<>(); Collections.addAll(keysList, keys); - return new DashDownloadAction(uri, isRemoveAction, data, keysList); + DownloadAction result; + if (isRemoveAction) { + result = DashDownloadAction.createRemoveAction(uri, data); + } else { + result = DashDownloadAction.createDownloadAction(uri, data, keysList); + } + return result; } } diff --git a/library/hls/build.gradle b/library/hls/build.gradle index 268d2faaca..af02544619 100644 --- a/library/hls/build.gradle +++ b/library/hls/build.gradle @@ -26,6 +26,7 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' } buildTypes { diff --git a/library/hls/proguard-rules.txt b/library/hls/proguard-rules.txt new file mode 100644 index 0000000000..3b8d1bb4ac --- /dev/null +++ b/library/hls/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the hls module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadAction.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadAction.java index 1e9825936a..c54a9a7dd3 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadAction.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadAction.java @@ -22,21 +22,24 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.SegmentDownloadAction; import com.google.android.exoplayer2.offline.StreamKey; import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.List; /** An action to download or remove downloaded HLS streams. */ public final class HlsDownloadAction extends SegmentDownloadAction { private static final String TYPE = "hls"; - private static final int VERSION = 0; + private static final int VERSION = 1; public static final Deserializer DESERIALIZER = new SegmentDownloadActionDeserializer(TYPE, VERSION) { @Override - protected StreamKey readKey(DataInputStream input) throws IOException { + protected StreamKey readKey(int version, DataInputStream input) throws IOException { + if (version > 0) { + return super.readKey(version, input); + } int renditionGroup = input.readInt(); int trackIndex = input.readInt(); return new StreamKey(renditionGroup, trackIndex); @@ -49,27 +52,46 @@ public final class HlsDownloadAction extends SegmentDownloadAction { } }; + /** + * Creates a HLS download action. + * + * @param uri The URI of the media to be downloaded. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + * @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded. + */ + public static HlsDownloadAction createDownloadAction( + Uri uri, @Nullable byte[] data, List keys) { + return new HlsDownloadAction(uri, /* isRemoveAction= */ false, data, keys); + } + + /** + * Creates a HLS remove action. + * + * @param uri The URI of the media to be removed. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + */ + public static HlsDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) { + return new HlsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + } + /** * @param uri The HLS playlist URI. * @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded. * @param data Optional custom data for this action. * @param keys Keys of renditions to be downloaded. If empty, all renditions are downloaded. If * {@code removeAction} is true, {@code keys} must empty. + * @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link + * #createRemoveAction(Uri, byte[])}. */ + @Deprecated public HlsDownloadAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, List keys) { super(TYPE, VERSION, uri, isRemoveAction, data, keys); } @Override - protected HlsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { + public HlsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { return new HlsDownloader(uri, keys, constructorHelper); } - @Override - protected void writeKey(DataOutputStream output, StreamKey key) throws IOException { - output.writeInt(key.groupIndex); - output.writeInt(key.trackIndex); - } - } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadHelper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadHelper.java index dd30341210..fcbe06993e 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadHelper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadHelper.java @@ -34,7 +34,6 @@ import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -100,13 +99,13 @@ public final class HlsDownloadHelper extends DownloadHelper { @Override public HlsDownloadAction getDownloadAction(@Nullable byte[] data, List trackKeys) { Assertions.checkNotNull(renditionGroups); - return new HlsDownloadAction( - uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys, renditionGroups)); + return HlsDownloadAction.createDownloadAction( + uri, data, toStreamKeys(trackKeys, renditionGroups)); } @Override public HlsDownloadAction getRemoveAction(@Nullable byte[] data) { - return new HlsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + return HlsDownloadAction.createRemoveAction(uri, data); } private static Format[] toFormats(List hlsUrls) { diff --git a/library/hls/src/main/proguard-rules.txt b/library/hls/src/main/proguard-rules.txt new file mode 100644 index 0000000000..3b8d1bb4ac --- /dev/null +++ b/library/hls/src/main/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the hls module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadActionTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadActionTest.java new file mode 100644 index 0000000000..778ecadddd --- /dev/null +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadActionTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 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.source.hls.offline; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.StreamKey; +import com.google.android.exoplayer2.upstream.DummyDataSource; +import com.google.android.exoplayer2.upstream.cache.Cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** Unit tests for {@link HlsDownloadAction}. */ +@RunWith(RobolectricTestRunner.class) +public class HlsDownloadActionTest { + + private Uri uri1; + private Uri uri2; + + @Before + public void setUp() { + uri1 = Uri.parse("http://test1.uri"); + uri2 = Uri.parse("http://test2.uri"); + } + + @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 testCreateDownloader() { + MockitoAnnotations.initMocks(this); + DownloadAction action = createDownloadAction(uri1); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY); + assertThat(action.createDownloader(constructorHelper)).isNotNull(); + } + + @Test + public void testSameUriDifferentAction_IsSameMedia() { + DownloadAction action1 = createRemoveAction(uri1); + DownloadAction action2 = createDownloadAction(uri1); + assertThat(action1.isSameMedia(action2)).isTrue(); + } + + @Test + public void testDifferentUriAndAction_IsNotSameMedia() { + DownloadAction action3 = createRemoveAction(uri2); + DownloadAction action4 = createDownloadAction(uri1); + assertThat(action3.isSameMedia(action4)).isFalse(); + } + + @SuppressWarnings("EqualsWithItself") + @Test + public void testEquals() { + DownloadAction action1 = createRemoveAction(uri1); + assertThat(action1.equals(action1)).isTrue(); + + DownloadAction action2 = createRemoveAction(uri1); + DownloadAction action3 = createRemoveAction(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)); + assertNotEqual(action6, action7); + + DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1)); + DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0)); + assertNotEqual(action8, action9); + + DownloadAction action10 = createRemoveAction(uri1); + DownloadAction action11 = createRemoveAction(uri2); + assertNotEqual(action10, action11); + + DownloadAction action12 = createDownloadAction(uri1, new StreamKey(0, 0), new StreamKey(1, 1)); + DownloadAction action13 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0)); + assertEqual(action12, action13); + + DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0)); + DownloadAction action15 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0)); + assertNotEqual(action14, action15); + + DownloadAction action16 = createDownloadAction(uri1); + DownloadAction action17 = createDownloadAction(uri1); + assertEqual(action16, action17); + } + + @Test + public void testSerializerGetType() { + DownloadAction action = createDownloadAction(uri1); + assertThat(action.type).isNotNull(); + } + + @Test + public void testSerializerWriteRead() throws Exception { + doTestSerializationRoundTrip(createDownloadAction(uri1)); + doTestSerializationRoundTrip(createRemoveAction(uri1)); + doTestSerializationRoundTrip( + createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1))); + } + + @Test + public void testSerializerVersion0() throws Exception { + doTestSerializationV0RoundTrip(createDownloadAction(uri1)); + doTestSerializationV0RoundTrip(createRemoveAction(uri1)); + doTestSerializationV0RoundTrip( + createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1))); + } + + private static void assertNotEqual(DownloadAction action1, DownloadAction action2) { + assertThat(action1).isNotEqualTo(action2); + assertThat(action2).isNotEqualTo(action1); + } + + private static void assertEqual(DownloadAction action1, DownloadAction action2) { + assertThat(action1).isEqualTo(action2); + assertThat(action2).isEqualTo(action1); + } + + private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + DownloadAction.serializeToStream(action, output); + + assertEqual(action, deserializeActionFromStream(out)); + } + + private static void doTestSerializationV0RoundTrip(HlsDownloadAction action) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + DataOutputStream dataOutputStream = new DataOutputStream(output); + dataOutputStream.writeUTF(action.type); + dataOutputStream.writeInt(/* version */ 0); + dataOutputStream.writeUTF(action.uri.toString()); + dataOutputStream.writeBoolean(action.isRemoveAction); + dataOutputStream.writeInt(action.data.length); + dataOutputStream.write(action.data); + dataOutputStream.writeInt(action.keys.size()); + for (int i = 0; i < action.keys.size(); i++) { + StreamKey key = action.keys.get(i); + dataOutputStream.writeInt(key.groupIndex); + dataOutputStream.writeInt(key.trackIndex); + } + dataOutputStream.flush(); + + assertEqual(action, deserializeActionFromStream(out)); + } + + private static DownloadAction deserializeActionFromStream(ByteArrayOutputStream out) + throws IOException { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + DataInputStream input = new DataInputStream(in); + return DownloadAction.deserializeFromStream( + new DownloadAction.Deserializer[] {HlsDownloadAction.DESERIALIZER}, input); + } + + private static HlsDownloadAction createDownloadAction(Uri uri, StreamKey... keys) { + ArrayList keysList = new ArrayList<>(); + Collections.addAll(keysList, keys); + return HlsDownloadAction.createDownloadAction(uri, null, keysList); + } + + private static HlsDownloadAction createRemoveAction(Uri uri) { + return HlsDownloadAction.createRemoveAction(uri, null); + } +} diff --git a/library/smoothstreaming/build.gradle b/library/smoothstreaming/build.gradle index ce7c2f6e3d..2fce6b697c 100644 --- a/library/smoothstreaming/build.gradle +++ b/library/smoothstreaming/build.gradle @@ -26,6 +26,7 @@ android { defaultConfig { minSdkVersion project.ext.minSdkVersion targetSdkVersion project.ext.targetSdkVersion + consumerProguardFiles 'proguard-rules.txt' } buildTypes { diff --git a/library/smoothstreaming/proguard-rules.txt b/library/smoothstreaming/proguard-rules.txt new file mode 100644 index 0000000000..d14244d783 --- /dev/null +++ b/library/smoothstreaming/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the smoothstreaming module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadAction.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadAction.java index 6f937a0496..ad2196fd74 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadAction.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadAction.java @@ -22,21 +22,24 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.SegmentDownloadAction; import com.google.android.exoplayer2.offline.StreamKey; import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.List; /** An action to download or remove downloaded SmoothStreaming streams. */ public final class SsDownloadAction extends SegmentDownloadAction { private static final String TYPE = "ss"; - private static final int VERSION = 0; + private static final int VERSION = 1; public static final Deserializer DESERIALIZER = new SegmentDownloadActionDeserializer(TYPE, VERSION) { @Override - protected StreamKey readKey(DataInputStream input) throws IOException { + protected StreamKey readKey(int version, DataInputStream input) throws IOException { + if (version > 0) { + return super.readKey(version, input); + } int groupIndex = input.readInt(); int trackIndex = input.readInt(); return new StreamKey(groupIndex, trackIndex); @@ -49,27 +52,46 @@ public final class SsDownloadAction extends SegmentDownloadAction { } }; + /** + * Creates a SmoothStreaming download action. + * + * @param uri The URI of the media to be downloaded. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + * @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded. + */ + public static SsDownloadAction createDownloadAction( + Uri uri, @Nullable byte[] data, List keys) { + return new SsDownloadAction(uri, /* isRemoveAction= */ false, data, keys); + } + + /** + * Creates a SmoothStreaming remove action. + * + * @param uri The URI of the media to be removed. + * @param data Optional custom data for this action. If {@code null} an empty array will be used. + */ + public static SsDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) { + return new SsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + } + /** * @param uri The SmoothStreaming manifest URI. * @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded. * @param data Optional custom data for this action. * @param keys Keys of streams to be downloaded. If empty, all streams are downloaded. If {@code * removeAction} is true, {@code keys} must be empty. + * @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link + * #createRemoveAction(Uri, byte[])}. */ + @Deprecated public SsDownloadAction( Uri uri, boolean isRemoveAction, @Nullable byte[] data, List keys) { super(TYPE, VERSION, uri, isRemoveAction, data, keys); } @Override - protected SsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { + public SsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) { return new SsDownloader(uri, keys, constructorHelper); } - @Override - protected void writeKey(DataOutputStream output, StreamKey key) throws IOException { - output.writeInt(key.groupIndex); - output.writeInt(key.trackIndex); - } - } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java index 9932ecd955..5125beff1c 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java @@ -30,7 +30,6 @@ import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -78,12 +77,12 @@ public final class SsDownloadHelper extends DownloadHelper { @Override public SsDownloadAction getDownloadAction(@Nullable byte[] data, List trackKeys) { - return new SsDownloadAction(uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys)); + return SsDownloadAction.createDownloadAction(uri, data, toStreamKeys(trackKeys)); } @Override public SsDownloadAction getRemoveAction(@Nullable byte[] data) { - return new SsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList()); + return SsDownloadAction.createRemoveAction(uri, data); } private static List toStreamKeys(List trackKeys) { diff --git a/library/smoothstreaming/src/main/proguard-rules.txt b/library/smoothstreaming/src/main/proguard-rules.txt new file mode 100644 index 0000000000..d14244d783 --- /dev/null +++ b/library/smoothstreaming/src/main/proguard-rules.txt @@ -0,0 +1,7 @@ +# Proguard rules specific to the smoothstreaming module. + +# Constructors accessed via reflection in SegmentDownloadAction +-dontnote com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction +-keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction { + static ** DESERIALIZER; +} diff --git a/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadActionTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadActionTest.java new file mode 100644 index 0000000000..fea03902ec --- /dev/null +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadActionTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 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.source.smoothstreaming.offline; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import com.google.android.exoplayer2.offline.DownloadAction; +import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; +import com.google.android.exoplayer2.offline.StreamKey; +import com.google.android.exoplayer2.upstream.DummyDataSource; +import com.google.android.exoplayer2.upstream.cache.Cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +/** Unit tests for {@link SsDownloadAction}. */ +@RunWith(RobolectricTestRunner.class) +public class SsDownloadActionTest { + + private Uri uri1; + private Uri uri2; + + @Before + public void setUp() { + uri1 = Uri.parse("http://test/1.uri"); + uri2 = Uri.parse("http://test/2.uri"); + } + + @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 testCreateDownloader() { + MockitoAnnotations.initMocks(this); + DownloadAction action = createDownloadAction(uri1); + DownloaderConstructorHelper constructorHelper = + new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY); + assertThat(action.createDownloader(constructorHelper)).isNotNull(); + } + + @Test + public void testSameUriDifferentAction_IsSameMedia() { + DownloadAction action1 = createRemoveAction(uri1); + DownloadAction action2 = createDownloadAction(uri1); + assertThat(action1.isSameMedia(action2)).isTrue(); + } + + @Test + public void testDifferentUriAndAction_IsNotSameMedia() { + DownloadAction action3 = createRemoveAction(uri2); + DownloadAction action4 = createDownloadAction(uri1); + assertThat(action3.isSameMedia(action4)).isFalse(); + } + + @SuppressWarnings("EqualsWithItself") + @Test + public void testEquals() { + DownloadAction action1 = createRemoveAction(uri1); + assertThat(action1.equals(action1)).isTrue(); + + DownloadAction action2 = createRemoveAction(uri1); + DownloadAction action3 = createRemoveAction(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)); + assertNotEqual(action6, action7); + + DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1)); + DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0)); + assertNotEqual(action8, action9); + + DownloadAction action10 = createRemoveAction(uri1); + DownloadAction action11 = createRemoveAction(uri2); + assertNotEqual(action10, action11); + + DownloadAction action12 = createDownloadAction(uri1, new StreamKey(0, 0), new StreamKey(1, 1)); + DownloadAction action13 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0)); + assertEqual(action12, action13); + + DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0)); + DownloadAction action15 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0)); + assertNotEqual(action14, action15); + + DownloadAction action16 = createDownloadAction(uri1); + DownloadAction action17 = createDownloadAction(uri1); + assertEqual(action16, action17); + } + + @Test + public void testSerializerGetType() { + DownloadAction action = createDownloadAction(uri1); + assertThat(action.type).isNotNull(); + } + + @Test + public void testSerializerWriteRead() throws Exception { + doTestSerializationRoundTrip(createDownloadAction(uri1)); + doTestSerializationRoundTrip(createRemoveAction(uri1)); + doTestSerializationRoundTrip( + createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1))); + } + + @Test + public void testSerializerVersion0() throws Exception { + doTestSerializationV0RoundTrip(createDownloadAction(uri1)); + doTestSerializationV0RoundTrip(createRemoveAction(uri1)); + doTestSerializationV0RoundTrip( + createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1))); + } + + private static void assertNotEqual(DownloadAction action1, DownloadAction action2) { + assertThat(action1).isNotEqualTo(action2); + assertThat(action2).isNotEqualTo(action1); + } + + private static void assertEqual(DownloadAction action1, DownloadAction action2) { + assertThat(action1).isEqualTo(action2); + assertThat(action2).isEqualTo(action1); + } + + private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + DownloadAction.serializeToStream(action, output); + + assertEqual(action, deserializeActionFromStream(out)); + } + + private static void doTestSerializationV0RoundTrip(SsDownloadAction action) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream output = new DataOutputStream(out); + DataOutputStream dataOutputStream = new DataOutputStream(output); + dataOutputStream.writeUTF(action.type); + dataOutputStream.writeInt(/* version */ 0); + dataOutputStream.writeUTF(action.uri.toString()); + dataOutputStream.writeBoolean(action.isRemoveAction); + dataOutputStream.writeInt(action.data.length); + dataOutputStream.write(action.data); + dataOutputStream.writeInt(action.keys.size()); + for (int i = 0; i < action.keys.size(); i++) { + StreamKey key = action.keys.get(i); + dataOutputStream.writeInt(key.groupIndex); + dataOutputStream.writeInt(key.trackIndex); + } + dataOutputStream.flush(); + + assertEqual(action, deserializeActionFromStream(out)); + } + + private static DownloadAction deserializeActionFromStream(ByteArrayOutputStream out) + throws IOException { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + DataInputStream input = new DataInputStream(in); + return DownloadAction.deserializeFromStream( + new DownloadAction.Deserializer[] {SsDownloadAction.DESERIALIZER}, input); + } + + private static SsDownloadAction createDownloadAction(Uri uri, StreamKey... keys) { + ArrayList keysList = new ArrayList<>(); + Collections.addAll(keysList, keys); + return SsDownloadAction.createDownloadAction(uri, null, keysList); + } + + private static SsDownloadAction createRemoveAction(Uri uri) { + return SsDownloadAction.createRemoveAction(uri, null); + } +}