Commit b2494800 by christosts Committed by Oliver Woodman

Offline: store MIME type and keySetId

Replace `type` with (optional) `mimeType` and add `keySetId` in
DownloadRequest. The DownloadHelper infers the downloading method (DASH,
HLS, SmoothStreaming or Progressive) from the content's MIME type and
URI.

PiperOrigin-RevId: 322117384
parent c669756f
Showing with 534 additions and 309 deletions
...@@ -30,6 +30,8 @@ import android.content.pm.PackageManager; ...@@ -30,6 +30,8 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point; import android.graphics.Point;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
...@@ -2227,6 +2229,14 @@ public final class Util { ...@@ -2227,6 +2229,14 @@ public final class Util {
items.addAll(Math.min(newFromIndex, items.size()), removedItems); items.addAll(Math.min(newFromIndex, items.size()), removedItems);
} }
/** Returns whether the table exists in the database. */
public static boolean tableExists(SQLiteDatabase database, String tableName) {
long count =
DatabaseUtils.queryNumEntries(
database, "sqlite_master", "tbl_name = ?", new String[] {tableName});
return count > 0;
}
@Nullable @Nullable
private static String getSystemProperty(String name) { private static String getSystemProperty(String name) {
try { try {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import static com.google.android.exoplayer2.testutil.TestUtil.getInMemoryDatabaseProvider;
import static com.google.android.exoplayer2.util.Util.binarySearchCeil; import static com.google.android.exoplayer2.util.Util.binarySearchCeil;
import static com.google.android.exoplayer2.util.Util.binarySearchFloor; import static com.google.android.exoplayer2.util.Util.binarySearchFloor;
import static com.google.android.exoplayer2.util.Util.escapeFileName; import static com.google.android.exoplayer2.util.Util.escapeFileName;
...@@ -24,6 +25,7 @@ import static com.google.android.exoplayer2.util.Util.parseXsDuration; ...@@ -24,6 +25,7 @@ import static com.google.android.exoplayer2.util.Util.parseXsDuration;
import static com.google.android.exoplayer2.util.Util.unescapeFileName; import static com.google.android.exoplayer2.util.Util.unescapeFileName;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.database.sqlite.SQLiteDatabase;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.StrikethroughSpan; import android.text.style.StrikethroughSpan;
...@@ -997,6 +999,21 @@ public class UtilTest { ...@@ -997,6 +999,21 @@ public class UtilTest {
assertThat(Util.normalizeLanguageCode("hsn")).isEqualTo("zh-hsn"); assertThat(Util.normalizeLanguageCode("hsn")).isEqualTo("zh-hsn");
} }
@Test
public void tableExists_withExistingTable() {
SQLiteDatabase database = getInMemoryDatabaseProvider().getWritableDatabase();
database.execSQL("CREATE TABLE TestTable (ID INTEGER NOT NULL)");
assertThat(Util.tableExists(database, "TestTable")).isTrue();
}
@Test
public void tableExists_withNonExistingTable() {
SQLiteDatabase database = getInMemoryDatabaseProvider().getReadableDatabase();
assertThat(Util.tableExists(database, "table")).isFalse();
}
private static void assertEscapeUnescapeFileName(String fileName, String escapedFileName) { private static void assertEscapeUnescapeFileName(String fileName, String escapedFileName) {
assertThat(escapeFileName(fileName)).isEqualTo(escapedFileName); assertThat(escapeFileName(fileName)).isEqualTo(escapedFileName);
assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName); assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName);
......
...@@ -17,11 +17,10 @@ package com.google.android.exoplayer2.database; ...@@ -17,11 +17,10 @@ package com.google.android.exoplayer2.database;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException; import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -115,7 +114,7 @@ public final class VersionTable { ...@@ -115,7 +114,7 @@ public final class VersionTable {
SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid) SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid)
throws DatabaseIOException { throws DatabaseIOException {
try { try {
if (!tableExists(writableDatabase, TABLE_NAME)) { if (!Util.tableExists(writableDatabase, TABLE_NAME)) {
return; return;
} }
writableDatabase.delete( writableDatabase.delete(
...@@ -140,7 +139,7 @@ public final class VersionTable { ...@@ -140,7 +139,7 @@ public final class VersionTable {
public static int getVersion(SQLiteDatabase database, @Feature int feature, String instanceUid) public static int getVersion(SQLiteDatabase database, @Feature int feature, String instanceUid)
throws DatabaseIOException { throws DatabaseIOException {
try { try {
if (!tableExists(database, TABLE_NAME)) { if (!Util.tableExists(database, TABLE_NAME)) {
return VERSION_UNSET; return VERSION_UNSET;
} }
try (Cursor cursor = try (Cursor cursor =
...@@ -163,14 +162,6 @@ public final class VersionTable { ...@@ -163,14 +162,6 @@ public final class VersionTable {
} }
} }
@VisibleForTesting
/* package */ static boolean tableExists(SQLiteDatabase readableDatabase, String tableName) {
long count =
DatabaseUtils.queryNumEntries(
readableDatabase, "sqlite_master", "tbl_name = ?", new String[] {tableName});
return count > 0;
}
private static String[] featureAndInstanceUidArguments(int feature, String instance) { private static String[] featureAndInstanceUidArguments(int feature, String instance) {
return new String[] {Integer.toString(feature), instance}; return new String[] {Integer.toString(feature), instance};
} }
......
...@@ -19,6 +19,7 @@ import android.net.Uri; ...@@ -19,6 +19,7 @@ import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.offline.DownloadRequest.UnsupportedRequestException; import com.google.android.exoplayer2.offline.DownloadRequest.UnsupportedRequestException;
import com.google.android.exoplayer2.util.AtomicFile; import com.google.android.exoplayer2.util.AtomicFile;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.File; import java.io.File;
...@@ -37,6 +38,10 @@ import java.util.List; ...@@ -37,6 +38,10 @@ import java.util.List;
/* package */ final class ActionFile { /* package */ final class ActionFile {
private static final int VERSION = 0; private static final int VERSION = 0;
private static final String DOWNLOAD_TYPE_PROGRESSIVE = "progressive";
private static final String DOWNLOAD_TYPE_DASH = "dash";
private static final String DOWNLOAD_TYPE_HLS = "hls";
private static final String DOWNLOAD_TYPE_SS = "ss";
private final AtomicFile atomicFile; private final AtomicFile atomicFile;
...@@ -92,7 +97,7 @@ import java.util.List; ...@@ -92,7 +97,7 @@ import java.util.List;
} }
private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException { private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException {
String type = input.readUTF(); String downloadType = input.readUTF();
int version = input.readInt(); int version = input.readInt();
Uri uri = Uri.parse(input.readUTF()); Uri uri = Uri.parse(input.readUTF());
...@@ -108,21 +113,21 @@ import java.util.List; ...@@ -108,21 +113,21 @@ import java.util.List;
} }
// Serialized version 0 progressive actions did not contain keys. // Serialized version 0 progressive actions did not contain keys.
boolean isLegacyProgressive = version == 0 && DownloadRequest.TYPE_PROGRESSIVE.equals(type); boolean isLegacyProgressive = version == 0 && DOWNLOAD_TYPE_PROGRESSIVE.equals(downloadType);
List<StreamKey> keys = new ArrayList<>(); List<StreamKey> keys = new ArrayList<>();
if (!isLegacyProgressive) { if (!isLegacyProgressive) {
int keyCount = input.readInt(); int keyCount = input.readInt();
for (int i = 0; i < keyCount; i++) { for (int i = 0; i < keyCount; i++) {
keys.add(readKey(type, version, input)); keys.add(readKey(downloadType, version, input));
} }
} }
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key. // Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
boolean isLegacySegmented = boolean isLegacySegmented =
version < 2 version < 2
&& (DownloadRequest.TYPE_DASH.equals(type) && (DOWNLOAD_TYPE_DASH.equals(downloadType)
|| DownloadRequest.TYPE_HLS.equals(type) || DOWNLOAD_TYPE_HLS.equals(downloadType)
|| DownloadRequest.TYPE_SS.equals(type)); || DOWNLOAD_TYPE_SS.equals(downloadType));
@Nullable String customCacheKey = null; @Nullable String customCacheKey = null;
if (!isLegacySegmented) { if (!isLegacySegmented) {
customCacheKey = input.readBoolean() ? input.readUTF() : null; customCacheKey = input.readBoolean() ? input.readUTF() : null;
...@@ -135,7 +140,10 @@ import java.util.List; ...@@ -135,7 +140,10 @@ import java.util.List;
// Remove actions are not supported anymore. // Remove actions are not supported anymore.
throw new UnsupportedRequestException(); throw new UnsupportedRequestException();
} }
return new DownloadRequest(id, type, uri, keys, customCacheKey, data); // keySetId and mimeType were not supported. Set keySetId to null and try to infer the mime
// type from the download type.
return new DownloadRequest(
id, uri, inferMimeType(downloadType), keys, /* keySetId= */ null, customCacheKey, data);
} }
private static StreamKey readKey(String type, int version, DataInputStream input) private static StreamKey readKey(String type, int version, DataInputStream input)
...@@ -145,8 +153,7 @@ import java.util.List; ...@@ -145,8 +153,7 @@ import java.util.List;
int trackIndex; int trackIndex;
// Serialized version 0 HLS/SS actions did not contain a period index. // Serialized version 0 HLS/SS actions did not contain a period index.
if ((DownloadRequest.TYPE_HLS.equals(type) || DownloadRequest.TYPE_SS.equals(type)) if ((DOWNLOAD_TYPE_HLS.equals(type) || DOWNLOAD_TYPE_SS.equals(type)) && version == 0) {
&& version == 0) {
periodIndex = 0; periodIndex = 0;
groupIndex = input.readInt(); groupIndex = input.readInt();
trackIndex = input.readInt(); trackIndex = input.readInt();
...@@ -158,6 +165,20 @@ import java.util.List; ...@@ -158,6 +165,20 @@ import java.util.List;
return new StreamKey(periodIndex, groupIndex, trackIndex); return new StreamKey(periodIndex, groupIndex, trackIndex);
} }
private static String inferMimeType(String downloadType) {
switch (downloadType) {
case DOWNLOAD_TYPE_DASH:
return MimeTypes.APPLICATION_MPD;
case DOWNLOAD_TYPE_HLS:
return MimeTypes.APPLICATION_M3U8;
case DOWNLOAD_TYPE_SS:
return MimeTypes.APPLICATION_SS;
case DOWNLOAD_TYPE_PROGRESSIVE:
default:
return MimeTypes.VIDEO_UNKNOWN;
}
}
private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) { private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) {
return customCacheKey != null ? customCacheKey : uri.toString(); return customCacheKey != null ? customCacheKey : uri.toString();
} }
......
...@@ -29,6 +29,7 @@ import com.google.android.exoplayer2.database.VersionTable; ...@@ -29,6 +29,7 @@ import com.google.android.exoplayer2.database.VersionTable;
import com.google.android.exoplayer2.offline.Download.FailureReason; import com.google.android.exoplayer2.offline.Download.FailureReason;
import com.google.android.exoplayer2.offline.Download.State; import com.google.android.exoplayer2.offline.Download.State;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -38,10 +39,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -38,10 +39,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "Downloads"; private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "Downloads";
@VisibleForTesting /* package */ static final int TABLE_VERSION = 2; @VisibleForTesting /* package */ static final int TABLE_VERSION = 3;
private static final String COLUMN_ID = "id"; private static final String COLUMN_ID = "id";
private static final String COLUMN_TYPE = "title"; private static final String COLUMN_MIME_TYPE = "mime_type";
private static final String COLUMN_URI = "uri"; private static final String COLUMN_URI = "uri";
private static final String COLUMN_STREAM_KEYS = "stream_keys"; private static final String COLUMN_STREAM_KEYS = "stream_keys";
private static final String COLUMN_CUSTOM_CACHE_KEY = "custom_cache_key"; private static final String COLUMN_CUSTOM_CACHE_KEY = "custom_cache_key";
...@@ -54,9 +55,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -54,9 +55,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
private static final String COLUMN_FAILURE_REASON = "failure_reason"; private static final String COLUMN_FAILURE_REASON = "failure_reason";
private static final String COLUMN_PERCENT_DOWNLOADED = "percent_downloaded"; private static final String COLUMN_PERCENT_DOWNLOADED = "percent_downloaded";
private static final String COLUMN_BYTES_DOWNLOADED = "bytes_downloaded"; private static final String COLUMN_BYTES_DOWNLOADED = "bytes_downloaded";
private static final String COLUMN_KEY_SET_ID = "key_set_id";
private static final int COLUMN_INDEX_ID = 0; private static final int COLUMN_INDEX_ID = 0;
private static final int COLUMN_INDEX_TYPE = 1; private static final int COLUMN_INDEX_MIME_TYPE = 1;
private static final int COLUMN_INDEX_URI = 2; private static final int COLUMN_INDEX_URI = 2;
private static final int COLUMN_INDEX_STREAM_KEYS = 3; private static final int COLUMN_INDEX_STREAM_KEYS = 3;
private static final int COLUMN_INDEX_CUSTOM_CACHE_KEY = 4; private static final int COLUMN_INDEX_CUSTOM_CACHE_KEY = 4;
...@@ -69,6 +71,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -69,6 +71,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
private static final int COLUMN_INDEX_FAILURE_REASON = 11; private static final int COLUMN_INDEX_FAILURE_REASON = 11;
private static final int COLUMN_INDEX_PERCENT_DOWNLOADED = 12; private static final int COLUMN_INDEX_PERCENT_DOWNLOADED = 12;
private static final int COLUMN_INDEX_BYTES_DOWNLOADED = 13; private static final int COLUMN_INDEX_BYTES_DOWNLOADED = 13;
private static final int COLUMN_INDEX_KEY_SET_ID = 14;
private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?"; private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?";
private static final String WHERE_STATE_IS_DOWNLOADING = private static final String WHERE_STATE_IS_DOWNLOADING =
...@@ -79,7 +82,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -79,7 +82,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
private static final String[] COLUMNS = private static final String[] COLUMNS =
new String[] { new String[] {
COLUMN_ID, COLUMN_ID,
COLUMN_TYPE, COLUMN_MIME_TYPE,
COLUMN_URI, COLUMN_URI,
COLUMN_STREAM_KEYS, COLUMN_STREAM_KEYS,
COLUMN_CUSTOM_CACHE_KEY, COLUMN_CUSTOM_CACHE_KEY,
...@@ -92,14 +95,15 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -92,14 +95,15 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
COLUMN_FAILURE_REASON, COLUMN_FAILURE_REASON,
COLUMN_PERCENT_DOWNLOADED, COLUMN_PERCENT_DOWNLOADED,
COLUMN_BYTES_DOWNLOADED, COLUMN_BYTES_DOWNLOADED,
COLUMN_KEY_SET_ID
}; };
private static final String TABLE_SCHEMA = private static final String TABLE_SCHEMA =
"(" "("
+ COLUMN_ID + COLUMN_ID
+ " TEXT PRIMARY KEY NOT NULL," + " TEXT PRIMARY KEY NOT NULL,"
+ COLUMN_TYPE + COLUMN_MIME_TYPE
+ " TEXT NOT NULL," + " TEXT,"
+ COLUMN_URI + COLUMN_URI
+ " TEXT NOT NULL," + " TEXT NOT NULL,"
+ COLUMN_STREAM_KEYS + COLUMN_STREAM_KEYS
...@@ -123,7 +127,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -123,7 +127,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
+ COLUMN_PERCENT_DOWNLOADED + COLUMN_PERCENT_DOWNLOADED
+ " REAL NOT NULL," + " REAL NOT NULL,"
+ COLUMN_BYTES_DOWNLOADED + COLUMN_BYTES_DOWNLOADED
+ " INTEGER NOT NULL)"; + " INTEGER NOT NULL,"
+ COLUMN_KEY_SET_ID
+ " BLOB NOT NULL)";
private static final String TRUE = "1"; private static final String TRUE = "1";
...@@ -189,24 +195,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -189,24 +195,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
@Override @Override
public void putDownload(Download download) throws DatabaseIOException { public void putDownload(Download download) throws DatabaseIOException {
ensureInitialized(); ensureInitialized();
ContentValues values = new ContentValues();
values.put(COLUMN_ID, download.request.id);
values.put(COLUMN_TYPE, download.request.type);
values.put(COLUMN_URI, download.request.uri.toString());
values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(download.request.streamKeys));
values.put(COLUMN_CUSTOM_CACHE_KEY, download.request.customCacheKey);
values.put(COLUMN_DATA, download.request.data);
values.put(COLUMN_STATE, download.state);
values.put(COLUMN_START_TIME_MS, download.startTimeMs);
values.put(COLUMN_UPDATE_TIME_MS, download.updateTimeMs);
values.put(COLUMN_CONTENT_LENGTH, download.contentLength);
values.put(COLUMN_STOP_REASON, download.stopReason);
values.put(COLUMN_FAILURE_REASON, download.failureReason);
values.put(COLUMN_PERCENT_DOWNLOADED, download.getPercentDownloaded());
values.put(COLUMN_BYTES_DOWNLOADED, download.getBytesDownloaded());
try { try {
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
writableDatabase.replaceOrThrow(tableName, /* nullColumnHack= */ null, values); putDownloadInternal(download, writableDatabase);
} catch (SQLiteException e) { } catch (SQLiteException e) {
throw new DatabaseIOException(e); throw new DatabaseIOException(e);
} }
...@@ -294,8 +285,13 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -294,8 +285,13 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
try { try {
VersionTable.setVersion( VersionTable.setVersion(
writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION); writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION);
List<Download> upgradedDownloads =
version == 2 ? loadDownloadsFromVersion2(writableDatabase) : new ArrayList<>();
writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName); writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName);
writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA); writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA);
for (Download download : upgradedDownloads) {
putDownloadInternal(download, writableDatabase);
}
writableDatabase.setTransactionSuccessful(); writableDatabase.setTransactionSuccessful();
} finally { } finally {
writableDatabase.endTransaction(); writableDatabase.endTransaction();
...@@ -307,6 +303,78 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -307,6 +303,78 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
} }
} }
private void putDownloadInternal(Download download, SQLiteDatabase database) {
ContentValues values = new ContentValues();
values.put(COLUMN_ID, download.request.id);
values.put(COLUMN_MIME_TYPE, download.request.mimeType);
values.put(COLUMN_URI, download.request.uri.toString());
values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(download.request.streamKeys));
values.put(COLUMN_CUSTOM_CACHE_KEY, download.request.customCacheKey);
values.put(COLUMN_DATA, download.request.data);
values.put(COLUMN_STATE, download.state);
values.put(COLUMN_START_TIME_MS, download.startTimeMs);
values.put(COLUMN_UPDATE_TIME_MS, download.updateTimeMs);
values.put(COLUMN_CONTENT_LENGTH, download.contentLength);
values.put(COLUMN_STOP_REASON, download.stopReason);
values.put(COLUMN_FAILURE_REASON, download.failureReason);
values.put(COLUMN_PERCENT_DOWNLOADED, download.getPercentDownloaded());
values.put(COLUMN_BYTES_DOWNLOADED, download.getBytesDownloaded());
values.put(COLUMN_KEY_SET_ID, download.request.keySetId);
database.replaceOrThrow(tableName, /* nullColumnHack= */ null, values);
}
private List<Download> loadDownloadsFromVersion2(SQLiteDatabase database) {
List<Download> downloads = new ArrayList<>();
if (!Util.tableExists(database, tableName)) {
return downloads;
}
String[] columnsV2 =
new String[] {
"id",
"title",
"uri",
"stream_keys",
"custom_cache_key",
"data",
"state",
"start_time_ms",
"update_time_ms",
"content_length",
"stop_reason",
"failure_reason",
"percent_downloaded",
"bytes_downloaded"
};
try (Cursor cursor =
database.query(
tableName,
columnsV2,
/* selection= */ null,
/* selectionArgs= */ null,
/* groupBy= */ null,
/* having= */ null,
/* orderBy= */ null); ) {
while (cursor.moveToNext()) {
downloads.add(getDownloadForCurrentRowV2(cursor));
}
return downloads;
}
}
/** Infers the MIME type from a v2 table row. */
private static String inferMimeType(String downloadType) {
if ("dash".equals(downloadType)) {
return MimeTypes.APPLICATION_MPD;
} else if ("hls".equals(downloadType)) {
return MimeTypes.APPLICATION_M3U8;
} else if ("ss".equals(downloadType)) {
return MimeTypes.APPLICATION_SS;
} else {
return MimeTypes.VIDEO_UNKNOWN;
}
}
private Cursor getCursor(String selection, @Nullable String[] selectionArgs) private Cursor getCursor(String selection, @Nullable String[] selectionArgs)
throws DatabaseIOException { throws DatabaseIOException {
try { try {
...@@ -326,6 +394,25 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -326,6 +394,25 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
} }
} }
@VisibleForTesting
/* package*/ static String encodeStreamKeys(List<StreamKey> streamKeys) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < streamKeys.size(); i++) {
StreamKey streamKey = streamKeys.get(i);
stringBuilder
.append(streamKey.periodIndex)
.append('.')
.append(streamKey.groupIndex)
.append('.')
.append(streamKey.trackIndex)
.append(',');
}
if (stringBuilder.length() > 0) {
stringBuilder.setLength(stringBuilder.length() - 1);
}
return stringBuilder.toString();
}
private static String getStateQuery(@Download.State int... states) { private static String getStateQuery(@Download.State int... states) {
if (states.length == 0) { if (states.length == 0) {
return TRUE; return TRUE;
...@@ -346,9 +433,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -346,9 +433,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
DownloadRequest request = DownloadRequest request =
new DownloadRequest( new DownloadRequest(
/* id= */ cursor.getString(COLUMN_INDEX_ID), /* id= */ cursor.getString(COLUMN_INDEX_ID),
/* type= */ cursor.getString(COLUMN_INDEX_TYPE),
/* uri= */ Uri.parse(cursor.getString(COLUMN_INDEX_URI)), /* uri= */ Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
/* mimeType= */ cursor.getString(COLUMN_INDEX_MIME_TYPE),
/* streamKeys= */ decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)), /* streamKeys= */ decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)),
/* keySetId= */ cursor.getBlob(COLUMN_INDEX_KEY_SET_ID),
/* customCacheKey= */ cursor.getString(COLUMN_INDEX_CUSTOM_CACHE_KEY), /* customCacheKey= */ cursor.getString(COLUMN_INDEX_CUSTOM_CACHE_KEY),
/* data= */ cursor.getBlob(COLUMN_INDEX_DATA)); /* data= */ cursor.getBlob(COLUMN_INDEX_DATA));
DownloadProgress downloadProgress = new DownloadProgress(); DownloadProgress downloadProgress = new DownloadProgress();
...@@ -373,22 +461,53 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { ...@@ -373,22 +461,53 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
downloadProgress); downloadProgress);
} }
private static String encodeStreamKeys(List<StreamKey> streamKeys) { /** Read a {@link Download} from a table row of version 2. */
StringBuilder stringBuilder = new StringBuilder(); private static Download getDownloadForCurrentRowV2(Cursor cursor) {
for (int i = 0; i < streamKeys.size(); i++) { /*
StreamKey streamKey = streamKeys.get(i); * Version 2 schema
stringBuilder * Index Column Type
.append(streamKey.periodIndex) * 0 id string
.append('.') * 1 type string
.append(streamKey.groupIndex) * 2 uri string
.append('.') * 3 stream_keys string
.append(streamKey.trackIndex) * 4 custom_cache_key string
.append(','); * 5 data blob
} * 6 state integer
if (stringBuilder.length() > 0) { * 7 start_time_ms integer
stringBuilder.setLength(stringBuilder.length() - 1); * 8 update_time_ms integer
} * 9 content_length integer
return stringBuilder.toString(); * 10 stop_reason integer
* 11 failure_reason integer
* 12 percent_downloaded real
* 13 bytes_downloaded integer
*/
DownloadRequest request =
new DownloadRequest(
/* id= */ cursor.getString(0),
/* uri= */ Uri.parse(cursor.getString(2)),
/* mimeType= */ inferMimeType(cursor.getString(1)),
/* streamKeys= */ decodeStreamKeys(cursor.getString(3)),
/* keySetId= */ null,
/* customCacheKey= */ cursor.getString(4),
/* data= */ cursor.getBlob(5));
DownloadProgress downloadProgress = new DownloadProgress();
downloadProgress.bytesDownloaded = cursor.getLong(13);
downloadProgress.percentDownloaded = cursor.getFloat(12);
@State int state = cursor.getInt(6);
// It's possible the database contains failure reasons for non-failed downloads, which is
// invalid. Clear them here. See https://github.com/google/ExoPlayer/issues/6785.
@FailureReason
int failureReason =
state == Download.STATE_FAILED ? cursor.getInt(11) : Download.FAILURE_REASON_NONE;
return new Download(
request,
state,
/* startTimeMs= */ cursor.getLong(7),
/* updateTimeMs= */ cursor.getLong(8),
/* contentLength= */ cursor.getLong(9),
/* stopReason= */ cursor.getInt(10),
failureReason,
downloadProgress);
} }
private static List<StreamKey> decodeStreamKeys(String encodedStreamKeys) { private static List<StreamKey> decodeStreamKeys(String encodedStreamKeys) {
......
...@@ -16,10 +16,13 @@ ...@@ -16,10 +16,13 @@
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import android.net.Uri; import android.net.Uri;
import android.util.SparseArray;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
...@@ -31,46 +34,8 @@ import java.util.concurrent.Executor; ...@@ -31,46 +34,8 @@ import java.util.concurrent.Executor;
*/ */
public class DefaultDownloaderFactory implements DownloaderFactory { public class DefaultDownloaderFactory implements DownloaderFactory {
@Nullable private static final Constructor<? extends Downloader> DASH_DOWNLOADER_CONSTRUCTOR; private static final SparseArray<Constructor<? extends Downloader>> CONSTRUCTORS =
@Nullable private static final Constructor<? extends Downloader> HLS_DOWNLOADER_CONSTRUCTOR; createDownloaderConstructors();
@Nullable private static final Constructor<? extends Downloader> SS_DOWNLOADER_CONSTRUCTOR;
static {
@Nullable Constructor<? extends Downloader> dashDownloaderConstructor = null;
try {
// LINT.IfChange
dashDownloaderConstructor =
getDownloaderConstructor(
Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloader"));
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) {
// Expected if the app was built without the DASH module.
}
DASH_DOWNLOADER_CONSTRUCTOR = dashDownloaderConstructor;
@Nullable Constructor<? extends Downloader> hlsDownloaderConstructor = null;
try {
// LINT.IfChange
hlsDownloaderConstructor =
getDownloaderConstructor(
Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloader"));
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) {
// Expected if the app was built without the HLS module.
}
HLS_DOWNLOADER_CONSTRUCTOR = hlsDownloaderConstructor;
@Nullable Constructor<? extends Downloader> ssDownloaderConstructor = null;
try {
// LINT.IfChange
ssDownloaderConstructor =
getDownloaderConstructor(
Class.forName(
"com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader"));
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) {
// Expected if the app was built without the SmoothStreaming module.
}
SS_DOWNLOADER_CONSTRUCTOR = ssDownloaderConstructor;
}
private final CacheDataSource.Factory cacheDataSourceFactory; private final CacheDataSource.Factory cacheDataSourceFactory;
private final Executor executor; private final Executor executor;
...@@ -105,8 +70,14 @@ public class DefaultDownloaderFactory implements DownloaderFactory { ...@@ -105,8 +70,14 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
@Override @Override
public Downloader createDownloader(DownloadRequest request) { public Downloader createDownloader(DownloadRequest request) {
switch (request.type) { @C.ContentType
case DownloadRequest.TYPE_PROGRESSIVE: int contentType = Util.inferContentTypeWithMimeType(request.uri, request.mimeType);
switch (contentType) {
case C.TYPE_DASH:
case C.TYPE_HLS:
case C.TYPE_SS:
return createDownloader(request, contentType);
case C.TYPE_OTHER:
return new ProgressiveDownloader( return new ProgressiveDownloader(
new MediaItem.Builder() new MediaItem.Builder()
.setUri(request.uri) .setUri(request.uri)
...@@ -114,31 +85,57 @@ public class DefaultDownloaderFactory implements DownloaderFactory { ...@@ -114,31 +85,57 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
.build(), .build(),
cacheDataSourceFactory, cacheDataSourceFactory,
executor); executor);
case DownloadRequest.TYPE_DASH:
return createDownloader(request, DASH_DOWNLOADER_CONSTRUCTOR);
case DownloadRequest.TYPE_HLS:
return createDownloader(request, HLS_DOWNLOADER_CONSTRUCTOR);
case DownloadRequest.TYPE_SS:
return createDownloader(request, SS_DOWNLOADER_CONSTRUCTOR);
default: default:
throw new IllegalArgumentException("Unsupported type: " + request.type); throw new IllegalArgumentException("Unsupported type: " + contentType);
} }
} }
private Downloader createDownloader( private Downloader createDownloader(DownloadRequest request, @C.ContentType int contentType) {
DownloadRequest request, @Nullable Constructor<? extends Downloader> constructor) { @Nullable Constructor<? extends Downloader> constructor = CONSTRUCTORS.get(contentType);
if (constructor == null) { if (constructor == null) {
throw new IllegalStateException("Module missing for: " + request.type); throw new IllegalStateException("Module missing for content type " + contentType);
} }
try { try {
return constructor.newInstance( return constructor.newInstance(
request.uri, request.streamKeys, cacheDataSourceFactory, executor); request.uri, request.streamKeys, cacheDataSourceFactory, executor);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to instantiate downloader for: " + request.type, e); throw new IllegalStateException(
"Failed to instantiate downloader for content type " + contentType);
} }
} }
// LINT.IfChange // LINT.IfChange
private static SparseArray<Constructor<? extends Downloader>> createDownloaderConstructors() {
SparseArray<Constructor<? extends Downloader>> array = new SparseArray<>();
try {
array.put(
C.TYPE_DASH,
getDownloaderConstructor(
Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloader")));
} catch (ClassNotFoundException e) {
// Expected if the app was built without the DASH module.
}
try {
array.put(
C.TYPE_HLS,
getDownloaderConstructor(
Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloader")));
} catch (ClassNotFoundException e) {
// Expected if the app was built without the HLS module.
}
try {
array.put(
C.TYPE_SS,
getDownloaderConstructor(
Class.forName(
"com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader")));
} catch (ClassNotFoundException e) {
// Expected if the app was built without the SmoothStreaming module.
}
return array;
}
private static Constructor<? extends Downloader> getDownloaderConstructor(Class<?> clazz) { private static Constructor<? extends Downloader> getDownloaderConstructor(Class<?> clazz) {
try { try {
return clazz return clazz
...@@ -146,7 +143,7 @@ public class DefaultDownloaderFactory implements DownloaderFactory { ...@@ -146,7 +143,7 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
.getConstructor(Uri.class, List.class, CacheDataSource.Factory.class, Executor.class); .getConstructor(Uri.class, List.class, CacheDataSource.Factory.class, Executor.class);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// The downloader is present, but the expected constructor is missing. // The downloader is present, but the expected constructor is missing.
throw new RuntimeException("Downloader constructor missing", e); throw new IllegalStateException("Downloader constructor missing", e);
} }
} }
// LINT.ThenChange(../../../../../../../../proguard-rules.txt) // LINT.ThenChange(../../../../../../../../proguard-rules.txt)
......
...@@ -320,9 +320,7 @@ public final class DownloadHelper { ...@@ -320,9 +320,7 @@ public final class DownloadHelper {
* @throws IllegalStateException If the media item is of type DASH, HLS or SmoothStreaming. * @throws IllegalStateException If the media item is of type DASH, HLS or SmoothStreaming.
*/ */
public static DownloadHelper forMediaItem(Context context, MediaItem mediaItem) { public static DownloadHelper forMediaItem(Context context, MediaItem mediaItem) {
Assertions.checkArgument( Assertions.checkArgument(isProgressive(checkNotNull(mediaItem.playbackProperties)));
DownloadRequest.TYPE_PROGRESSIVE.equals(
getDownloadType(checkNotNull(mediaItem.playbackProperties))));
return forMediaItem( return forMediaItem(
mediaItem, mediaItem,
getDefaultTrackSelectorParameters(context), getDefaultTrackSelectorParameters(context),
...@@ -412,9 +410,7 @@ public final class DownloadHelper { ...@@ -412,9 +410,7 @@ public final class DownloadHelper {
@Nullable RenderersFactory renderersFactory, @Nullable RenderersFactory renderersFactory,
@Nullable DataSource.Factory dataSourceFactory, @Nullable DataSource.Factory dataSourceFactory,
@Nullable DrmSessionManager drmSessionManager) { @Nullable DrmSessionManager drmSessionManager) {
boolean isProgressive = boolean isProgressive = isProgressive(checkNotNull(mediaItem.playbackProperties));
DownloadRequest.TYPE_PROGRESSIVE.equals(
getDownloadType(checkNotNull(mediaItem.playbackProperties)));
Assertions.checkArgument(isProgressive || dataSourceFactory != null); Assertions.checkArgument(isProgressive || dataSourceFactory != null);
return new DownloadHelper( return new DownloadHelper(
mediaItem, mediaItem,
...@@ -455,7 +451,7 @@ public final class DownloadHelper { ...@@ -455,7 +451,7 @@ public final class DownloadHelper {
new MediaItem.Builder() new MediaItem.Builder()
.setUri(downloadRequest.uri) .setUri(downloadRequest.uri)
.setCustomCacheKey(downloadRequest.customCacheKey) .setCustomCacheKey(downloadRequest.customCacheKey)
.setMimeType(getMimeType(downloadRequest.type)) .setMimeType(downloadRequest.mimeType)
.setStreamKeys(downloadRequest.streamKeys) .setStreamKeys(downloadRequest.streamKeys)
.build(), .build(),
dataSourceFactory, dataSourceFactory,
...@@ -747,13 +743,14 @@ public final class DownloadHelper { ...@@ -747,13 +743,14 @@ public final class DownloadHelper {
* @return The built {@link DownloadRequest}. * @return The built {@link DownloadRequest}.
*/ */
public DownloadRequest getDownloadRequest(String id, @Nullable byte[] data) { public DownloadRequest getDownloadRequest(String id, @Nullable byte[] data) {
String downloadType = getDownloadType(playbackProperties);
if (mediaSource == null) { if (mediaSource == null) {
// TODO: add support for DRM (keySetId) [Internal ref: b/158980798]
return new DownloadRequest( return new DownloadRequest(
id, id,
downloadType,
playbackProperties.uri, playbackProperties.uri,
playbackProperties.mimeType,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* keySetId= */ null,
playbackProperties.customCacheKey, playbackProperties.customCacheKey,
data); data);
} }
...@@ -769,11 +766,13 @@ public final class DownloadHelper { ...@@ -769,11 +766,13 @@ public final class DownloadHelper {
} }
streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections)); streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections));
} }
// TODO: add support for DRM (keySetId) [Internal ref: b/158980798]
return new DownloadRequest( return new DownloadRequest(
id, id,
downloadType,
playbackProperties.uri, playbackProperties.uri,
playbackProperties.mimeType,
streamKeys, streamKeys,
/* keySetId= */ null,
playbackProperties.customCacheKey, playbackProperties.customCacheKey,
data); data);
} }
...@@ -909,36 +908,9 @@ public final class DownloadHelper { ...@@ -909,36 +908,9 @@ public final class DownloadHelper {
.createMediaSource(mediaItem); .createMediaSource(mediaItem);
} }
private static String getDownloadType(MediaItem.PlaybackProperties playbackProperties) { private static boolean isProgressive(MediaItem.PlaybackProperties playbackProperties) {
int contentType = return Util.inferContentTypeWithMimeType(playbackProperties.uri, playbackProperties.mimeType)
Util.inferContentTypeWithMimeType(playbackProperties.uri, playbackProperties.mimeType); == C.TYPE_OTHER;
switch (contentType) {
case C.TYPE_DASH:
return DownloadRequest.TYPE_DASH;
case C.TYPE_HLS:
return DownloadRequest.TYPE_HLS;
case C.TYPE_SS:
return DownloadRequest.TYPE_SS;
case C.TYPE_OTHER:
default:
return DownloadRequest.TYPE_PROGRESSIVE;
}
}
@Nullable
private static String getMimeType(String downloadType) {
switch (downloadType) {
case DownloadRequest.TYPE_DASH:
return MimeTypes.APPLICATION_MPD;
case DownloadRequest.TYPE_HLS:
return MimeTypes.APPLICATION_M3U8;
case DownloadRequest.TYPE_SS:
return MimeTypes.APPLICATION_SS;
case DownloadRequest.TYPE_PROGRESSIVE:
return null;
default:
throw new IllegalArgumentException();
}
} }
private static final class MediaPreparer private static final class MediaPreparer
......
...@@ -21,6 +21,7 @@ import android.net.Uri; ...@@ -21,6 +21,7 @@ import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
...@@ -35,24 +36,24 @@ public final class DownloadRequest implements Parcelable { ...@@ -35,24 +36,24 @@ public final class DownloadRequest implements Parcelable {
/** Thrown when the encoded request data belongs to an unsupported request type. */ /** Thrown when the encoded request data belongs to an unsupported request type. */
public static class UnsupportedRequestException extends IOException {} public static class UnsupportedRequestException extends IOException {}
/** Type for progressive downloads. */
public static final String TYPE_PROGRESSIVE = "progressive";
/** Type for DASH downloads. */
public static final String TYPE_DASH = "dash";
/** Type for HLS downloads. */
public static final String TYPE_HLS = "hls";
/** Type for SmoothStreaming downloads. */
public static final String TYPE_SS = "ss";
/** The unique content id. */ /** The unique content id. */
public final String id; public final String id;
/** The type of the request. */
public final String type;
/** The uri being downloaded. */ /** The uri being downloaded. */
public final Uri uri; public final Uri uri;
/**
* The MIME type of this content. Used as a hint to infer the content's type (DASH, HLS,
* SmoothStreaming). If null, a {@link DownloadService} will infer the content type from the
* {@link #uri}.
*/
@Nullable public final String mimeType;
/** Stream keys to be downloaded. If empty, all streams will be downloaded. */ /** Stream keys to be downloaded. If empty, all streams will be downloaded. */
public final List<StreamKey> streamKeys; public final List<StreamKey> streamKeys;
/** /**
* The key set id of the offline licence if the content is protected with DRM, or empty if no
* license is needed.
*/
public final byte[] keySetId;
/**
* Custom key for cache indexing, or null. Must be null for DASH, HLS and SmoothStreaming * Custom key for cache indexing, or null. Must be null for DASH, HLS and SmoothStreaming
* downloads. * downloads.
*/ */
...@@ -62,43 +63,48 @@ public final class DownloadRequest implements Parcelable { ...@@ -62,43 +63,48 @@ public final class DownloadRequest implements Parcelable {
/** /**
* @param id See {@link #id}. * @param id See {@link #id}.
* @param type See {@link #type}.
* @param uri See {@link #uri}. * @param uri See {@link #uri}.
* @param mimeType See {@link #mimeType}
* @param streamKeys See {@link #streamKeys}. * @param streamKeys See {@link #streamKeys}.
* @param customCacheKey See {@link #customCacheKey}. * @param customCacheKey See {@link #customCacheKey}.
* @param data See {@link #data}. * @param data See {@link #data}.
*/ */
public DownloadRequest( public DownloadRequest(
String id, String id,
String type,
Uri uri, Uri uri,
@Nullable String mimeType,
List<StreamKey> streamKeys, List<StreamKey> streamKeys,
@Nullable byte[] keySetId,
@Nullable String customCacheKey, @Nullable String customCacheKey,
@Nullable byte[] data) { @Nullable byte[] data) {
if (TYPE_DASH.equals(type) || TYPE_HLS.equals(type) || TYPE_SS.equals(type)) { @C.ContentType int contentType = Util.inferContentTypeWithMimeType(uri, mimeType);
if (contentType == C.TYPE_DASH || contentType == C.TYPE_HLS || contentType == C.TYPE_SS) {
Assertions.checkArgument( Assertions.checkArgument(
customCacheKey == null, "customCacheKey must be null for type: " + type); customCacheKey == null, "customCacheKey must be null for type: " + contentType);
} }
this.id = id; this.id = id;
this.type = type;
this.uri = uri; this.uri = uri;
this.mimeType = mimeType;
ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys); ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys);
Collections.sort(mutableKeys); Collections.sort(mutableKeys);
this.streamKeys = Collections.unmodifiableList(mutableKeys); this.streamKeys = Collections.unmodifiableList(mutableKeys);
this.keySetId =
keySetId != null ? Arrays.copyOf(keySetId, keySetId.length) : Util.EMPTY_BYTE_ARRAY;
this.customCacheKey = customCacheKey; this.customCacheKey = customCacheKey;
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY; this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
} }
/* package */ DownloadRequest(Parcel in) { /* package */ DownloadRequest(Parcel in) {
id = castNonNull(in.readString()); id = castNonNull(in.readString());
type = castNonNull(in.readString());
uri = Uri.parse(castNonNull(in.readString())); uri = Uri.parse(castNonNull(in.readString()));
mimeType = in.readString();
int streamKeyCount = in.readInt(); int streamKeyCount = in.readInt();
ArrayList<StreamKey> mutableStreamKeys = new ArrayList<>(streamKeyCount); ArrayList<StreamKey> mutableStreamKeys = new ArrayList<>(streamKeyCount);
for (int i = 0; i < streamKeyCount; i++) { for (int i = 0; i < streamKeyCount; i++) {
mutableStreamKeys.add(in.readParcelable(StreamKey.class.getClassLoader())); mutableStreamKeys.add(in.readParcelable(StreamKey.class.getClassLoader()));
} }
streamKeys = Collections.unmodifiableList(mutableStreamKeys); streamKeys = Collections.unmodifiableList(mutableStreamKeys);
keySetId = castNonNull(in.createByteArray());
customCacheKey = in.readString(); customCacheKey = in.readString();
data = castNonNull(in.createByteArray()); data = castNonNull(in.createByteArray());
} }
...@@ -110,24 +116,22 @@ public final class DownloadRequest implements Parcelable { ...@@ -110,24 +116,22 @@ public final class DownloadRequest implements Parcelable {
* @return The copy with the specified ID. * @return The copy with the specified ID.
*/ */
public DownloadRequest copyWithId(String id) { public DownloadRequest copyWithId(String id) {
return new DownloadRequest(id, type, uri, streamKeys, customCacheKey, data); return new DownloadRequest(id, uri, mimeType, streamKeys, keySetId, customCacheKey, data);
} }
/** /**
* Returns the result of merging {@code newRequest} into this request. The requests must have the * Returns the result of merging {@code newRequest} into this request. The requests must have the
* same {@link #id} and {@link #type}. * same {@link #id}.
* *
* <p>If the requests have different {@link #uri}, {@link #customCacheKey} and {@link #data} * <p>The resulting request contains the stream keys from both requests. For all other member
* values, then those from the request being merged are included in the result. * variables, those in {@code newRequest} are preferred.
* *
* @param newRequest The request being merged. * @param newRequest The request being merged.
* @return The merged result. * @return The merged result.
* @throws IllegalArgumentException If the requests do not have the same {@link #id} and {@link * @throws IllegalArgumentException If the requests do not have the same {@link #id}.
* #type}.
*/ */
public DownloadRequest copyWithMergedRequest(DownloadRequest newRequest) { public DownloadRequest copyWithMergedRequest(DownloadRequest newRequest) {
Assertions.checkArgument(id.equals(newRequest.id)); Assertions.checkArgument(id.equals(newRequest.id));
Assertions.checkArgument(type.equals(newRequest.type));
List<StreamKey> mergedKeys; List<StreamKey> mergedKeys;
if (streamKeys.isEmpty() || newRequest.streamKeys.isEmpty()) { if (streamKeys.isEmpty() || newRequest.streamKeys.isEmpty()) {
// If either streamKeys is empty then all streams should be downloaded. // If either streamKeys is empty then all streams should be downloaded.
...@@ -142,12 +146,18 @@ public final class DownloadRequest implements Parcelable { ...@@ -142,12 +146,18 @@ public final class DownloadRequest implements Parcelable {
} }
} }
return new DownloadRequest( return new DownloadRequest(
id, type, newRequest.uri, mergedKeys, newRequest.customCacheKey, newRequest.data); id,
newRequest.uri,
newRequest.mimeType,
mergedKeys,
newRequest.keySetId,
newRequest.customCacheKey,
newRequest.data);
} }
@Override @Override
public String toString() { public String toString() {
return type + ":" + id; return mimeType + ":" + id;
} }
@Override @Override
...@@ -157,20 +167,21 @@ public final class DownloadRequest implements Parcelable { ...@@ -157,20 +167,21 @@ public final class DownloadRequest implements Parcelable {
} }
DownloadRequest that = (DownloadRequest) o; DownloadRequest that = (DownloadRequest) o;
return id.equals(that.id) return id.equals(that.id)
&& type.equals(that.type)
&& uri.equals(that.uri) && uri.equals(that.uri)
&& Util.areEqual(mimeType, that.mimeType)
&& streamKeys.equals(that.streamKeys) && streamKeys.equals(that.streamKeys)
&& Arrays.equals(keySetId, that.keySetId)
&& Util.areEqual(customCacheKey, that.customCacheKey) && Util.areEqual(customCacheKey, that.customCacheKey)
&& Arrays.equals(data, that.data); && Arrays.equals(data, that.data);
} }
@Override @Override
public final int hashCode() { public final int hashCode() {
int result = type.hashCode(); int result = 31 * id.hashCode();
result = 31 * result + id.hashCode();
result = 31 * result + type.hashCode();
result = 31 * result + uri.hashCode(); result = 31 * result + uri.hashCode();
result = 31 * result + (mimeType != null ? mimeType.hashCode() : 0);
result = 31 * result + streamKeys.hashCode(); result = 31 * result + streamKeys.hashCode();
result = 31 * result + Arrays.hashCode(keySetId);
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0); result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
result = 31 * result + Arrays.hashCode(data); result = 31 * result + Arrays.hashCode(data);
return result; return result;
...@@ -186,12 +197,13 @@ public final class DownloadRequest implements Parcelable { ...@@ -186,12 +197,13 @@ public final class DownloadRequest implements Parcelable {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id); dest.writeString(id);
dest.writeString(type);
dest.writeString(uri.toString()); dest.writeString(uri.toString());
dest.writeString(mimeType);
dest.writeInt(streamKeys.size()); dest.writeInt(streamKeys.size());
for (int i = 0; i < streamKeys.size(); i++) { for (int i = 0; i < streamKeys.size(); i++) {
dest.writeParcelable(streamKeys.get(i), /* parcelableFlags= */ 0); dest.writeParcelable(streamKeys.get(i), /* parcelableFlags= */ 0);
} }
dest.writeByteArray(keySetId);
dest.writeString(customCacheKey); dest.writeString(customCacheKey);
dest.writeByteArray(data); dest.writeByteArray(data);
} }
......
...@@ -85,16 +85,4 @@ public class VersionTableTest { ...@@ -85,16 +85,4 @@ public class VersionTableTest {
.isEqualTo(VersionTable.VERSION_UNSET); .isEqualTo(VersionTable.VERSION_UNSET);
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2); assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2);
} }
@Test
public void doesTableExist_nonExistingTable_returnsFalse() {
assertThat(VersionTable.tableExists(database, "NonExistingTable")).isFalse();
}
@Test
public void doesTableExist_existingTable_returnsTrue() {
String table = "TestTable";
databaseProvider.getWritableDatabase().execSQL("CREATE TABLE " + table + " (test INTEGER)");
assertThat(VersionTable.tableExists(database, table)).isTrue();
}
} }
...@@ -21,6 +21,7 @@ import android.net.Uri; ...@@ -21,6 +21,7 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
...@@ -127,9 +128,10 @@ public class ActionFileTest { ...@@ -127,9 +128,10 @@ public class ActionFileTest {
private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) { private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) {
return new DownloadRequest( return new DownloadRequest(
/* id= */ uri.toString(), /* id= */ uri.toString(),
DownloadRequest.TYPE_PROGRESSIVE,
uri, uri,
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
data); data);
} }
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_PROGRESSIVE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.net.Uri; import android.net.Uri;
...@@ -23,6 +22,7 @@ import androidx.test.core.app.ApplicationProvider; ...@@ -23,6 +22,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
...@@ -74,18 +74,20 @@ public class ActionFileUpgradeUtilTest { ...@@ -74,18 +74,20 @@ public class ActionFileUpgradeUtilTest {
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2); new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
DownloadRequest expectedRequest1 = DownloadRequest expectedRequest1 =
new DownloadRequest( new DownloadRequest(
"key123", /* id= */ "key123",
/* type= */ "test",
Uri.parse("https://www.test.com/download1"), Uri.parse("https://www.test.com/download1"),
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
asList(expectedStreamKey1), asList(expectedStreamKey1),
/* keySetId= */ null,
/* customCacheKey= */ "key123", /* customCacheKey= */ "key123",
new byte[] {1, 2, 3, 4}); /* data= */ new byte[] {1, 2, 3, 4});
DownloadRequest expectedRequest2 = DownloadRequest expectedRequest2 =
new DownloadRequest( new DownloadRequest(
"key234", /* id= */ "key234",
/* type= */ "test",
Uri.parse("https://www.test.com/download2"), Uri.parse("https://www.test.com/download2"),
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
asList(expectedStreamKey2), asList(expectedStreamKey2),
/* keySetId= */ null,
/* customCacheKey= */ "key234", /* customCacheKey= */ "key234",
new byte[] {5, 4, 3, 2, 1}); new byte[] {5, 4, 3, 2, 1});
...@@ -102,17 +104,17 @@ public class ActionFileUpgradeUtilTest { ...@@ -102,17 +104,17 @@ public class ActionFileUpgradeUtilTest {
@Test @Test
public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException { public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException {
byte[] data = new byte[] {1, 2, 3, 4};
DownloadRequest request = DownloadRequest request =
new DownloadRequest( new DownloadRequest(
"id", /* id= */ "id",
TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download"), Uri.parse("https://www.test.com/download"),
/* mimeType= */ null,
asList( asList(
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2), new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)), new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)),
/* keySetId= */ new byte[] {1, 2, 3, 4},
/* customCacheKey= */ "key123", /* customCacheKey= */ "key123",
data); /* data= */ new byte[] {1, 2, 3, 4});
ActionFileUpgradeUtil.mergeRequest( ActionFileUpgradeUtil.mergeRequest(
request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
...@@ -128,32 +130,36 @@ public class ActionFileUpgradeUtilTest { ...@@ -128,32 +130,36 @@ public class ActionFileUpgradeUtilTest {
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2); new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
DownloadRequest request1 = DownloadRequest request1 =
new DownloadRequest( new DownloadRequest(
"id", /* id= */ "id",
TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download1"), Uri.parse("https://www.test.com/download1"),
/* mimeType= */ null,
asList(streamKey1), asList(streamKey1),
/* keySetId= */ new byte[] {1, 2, 3, 4},
/* customCacheKey= */ "key123", /* customCacheKey= */ "key123",
new byte[] {1, 2, 3, 4}); /* data= */ new byte[] {1, 2, 3, 4});
DownloadRequest request2 = DownloadRequest request2 =
new DownloadRequest( new DownloadRequest(
"id", /* id= */ "id",
TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download2"), Uri.parse("https://www.test.com/download2"),
/* mimeType= */ MimeTypes.APPLICATION_MP4,
asList(streamKey2), asList(streamKey2),
/* customCacheKey= */ "key123", /* keySetId= */ new byte[] {5, 4, 3, 2, 1},
new byte[] {5, 4, 3, 2, 1}); /* customCacheKey= */ "key345",
/* data= */ new byte[] {5, 4, 3, 2, 1});
ActionFileUpgradeUtil.mergeRequest( ActionFileUpgradeUtil.mergeRequest(
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
ActionFileUpgradeUtil.mergeRequest( ActionFileUpgradeUtil.mergeRequest(
request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
Download download = downloadIndex.getDownload(request2.id); Download download = downloadIndex.getDownload(request2.id);
assertThat(download).isNotNull(); assertThat(download).isNotNull();
assertThat(download.request.type).isEqualTo(request2.type); assertThat(download.request.mimeType).isEqualTo(MimeTypes.APPLICATION_MP4);
assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey); assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey);
assertThat(download.request.data).isEqualTo(request2.data); assertThat(download.request.data).isEqualTo(request2.data);
assertThat(download.request.uri).isEqualTo(request2.uri); assertThat(download.request.uri).isEqualTo(request2.uri);
assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2); assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2);
assertThat(download.request.keySetId).isEqualTo(request2.keySetId);
assertThat(download.state).isEqualTo(Download.STATE_QUEUED); assertThat(download.state).isEqualTo(Download.STATE_QUEUED);
} }
...@@ -165,20 +171,22 @@ public class ActionFileUpgradeUtilTest { ...@@ -165,20 +171,22 @@ public class ActionFileUpgradeUtilTest {
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2); new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
DownloadRequest request1 = DownloadRequest request1 =
new DownloadRequest( new DownloadRequest(
"id1", /* id= */ "id1",
TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download1"), Uri.parse("https://www.test.com/download1"),
/* mimeType= */ null,
asList(streamKey1), asList(streamKey1),
/* keySetId= */ new byte[] {1, 2, 3, 4},
/* customCacheKey= */ "key123", /* customCacheKey= */ "key123",
new byte[] {1, 2, 3, 4}); /* data= */ new byte[] {1, 2, 3, 4});
DownloadRequest request2 = DownloadRequest request2 =
new DownloadRequest( new DownloadRequest(
"id2", /* id= */ "id2",
TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download2"), Uri.parse("https://www.test.com/download2"),
/* mimeType= */ null,
asList(streamKey2), asList(streamKey2),
/* keySetId= */ new byte[] {5, 4, 3, 2, 1},
/* customCacheKey= */ "key123", /* customCacheKey= */ "key123",
new byte[] {5, 4, 3, 2, 1}); /* data= */ new byte[] {5, 4, 3, 2, 1});
ActionFileUpgradeUtil.mergeRequest( ActionFileUpgradeUtil.mergeRequest(
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
......
...@@ -15,15 +15,31 @@ ...@@ -15,15 +15,31 @@
*/ */
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import static com.google.android.exoplayer2.offline.Download.FAILURE_REASON_NONE;
import static com.google.android.exoplayer2.offline.Download.FAILURE_REASON_UNKNOWN;
import static com.google.android.exoplayer2.offline.Download.STATE_DOWNLOADING;
import static com.google.android.exoplayer2.offline.Download.STATE_STOPPED;
import static com.google.android.exoplayer2.offline.Download.STOP_REASON_NONE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.database.DatabaseIOException; import com.google.android.exoplayer2.database.DatabaseIOException;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.database.VersionTable; import com.google.android.exoplayer2.database.VersionTable;
import com.google.android.exoplayer2.testutil.DownloadBuilder; import com.google.android.exoplayer2.testutil.DownloadBuilder;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -73,14 +89,14 @@ public class DefaultDownloadIndexTest { ...@@ -73,14 +89,14 @@ public class DefaultDownloadIndexTest {
Download download = Download download =
downloadBuilder downloadBuilder
.setType("different type")
.setUri("different uri") .setUri("different uri")
.setMimeType(MimeTypes.APPLICATION_MP4)
.setCacheKey("different cacheKey") .setCacheKey("different cacheKey")
.setState(Download.STATE_FAILED) .setState(Download.STATE_FAILED)
.setPercentDownloaded(50) .setPercentDownloaded(50)
.setBytesDownloaded(200) .setBytesDownloaded(200)
.setContentLength(400) .setContentLength(400)
.setFailureReason(Download.FAILURE_REASON_UNKNOWN) .setFailureReason(FAILURE_REASON_UNKNOWN)
.setStopReason(0x12345678) .setStopReason(0x12345678)
.setStartTimeMs(10) .setStartTimeMs(10)
.setUpdateTimeMs(20) .setUpdateTimeMs(20)
...@@ -88,6 +104,7 @@ public class DefaultDownloadIndexTest { ...@@ -88,6 +104,7 @@ public class DefaultDownloadIndexTest {
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2), new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)) new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5))
.setCustomMetadata(new byte[] {0, 1, 2, 3, 7, 8, 9, 10}) .setCustomMetadata(new byte[] {0, 1, 2, 3, 7, 8, 9, 10})
.setKeySetId(new byte[] {0, 1, 2, 3})
.build(); .build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
Download readDownload = downloadIndex.getDownload(id); Download readDownload = downloadIndex.getDownload(id);
...@@ -153,7 +170,7 @@ public class DefaultDownloadIndexTest { ...@@ -153,7 +170,7 @@ public class DefaultDownloadIndexTest {
new DownloadBuilder("id1").setStartTimeMs(0).setState(Download.STATE_REMOVING).build(); new DownloadBuilder("id1").setStartTimeMs(0).setState(Download.STATE_REMOVING).build();
downloadIndex.putDownload(download1); downloadIndex.putDownload(download1);
Download download2 = Download download2 =
new DownloadBuilder("id2").setStartTimeMs(1).setState(Download.STATE_STOPPED).build(); new DownloadBuilder("id2").setStartTimeMs(1).setState(STATE_STOPPED).build();
downloadIndex.putDownload(download2); downloadIndex.putDownload(download2);
Download download3 = Download download3 =
new DownloadBuilder("id3").setStartTimeMs(2).setState(Download.STATE_COMPLETED).build(); new DownloadBuilder("id3").setStartTimeMs(2).setState(Download.STATE_COMPLETED).build();
...@@ -203,6 +220,47 @@ public class DefaultDownloadIndexTest { ...@@ -203,6 +220,47 @@ public class DefaultDownloadIndexTest {
} }
@Test @Test
public void downloadIndex_upgradesFromVersion2() throws IOException {
Context context = ApplicationProvider.getApplicationContext();
File databaseFile = context.getDatabasePath(ExoDatabaseProvider.DATABASE_NAME);
try (FileOutputStream output = new FileOutputStream(databaseFile)) {
output.write(TestUtil.getByteArray(context, "offline/exoplayer_internal_v2.db"));
}
Download dashDownload =
createDownload(
/* uri= */ "http://www.test.com/manifest.mpd",
/* mimeType= */ MimeTypes.APPLICATION_MPD,
ImmutableList.of(),
/* customCacheKey= */ null);
Download hlsDownload =
createDownload(
/* uri= */ "http://www.test.com/manifest.m3u8",
/* mimeType= */ MimeTypes.APPLICATION_M3U8,
ImmutableList.of(),
/* customCacheKey= */ null);
Download ssDownload =
createDownload(
/* uri= */ "http://www.test.com/video.ism/manifest",
/* mimeType= */ MimeTypes.APPLICATION_SS,
Arrays.asList(new StreamKey(0, 0), new StreamKey(1, 1)),
/* customCacheKey= */ null);
Download progressiveDownload =
createDownload(
/* uri= */ "http://www.test.com/video.mp4",
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
ImmutableList.of(),
/* customCacheKey= */ "customCacheKey");
databaseProvider = new ExoDatabaseProvider(context);
downloadIndex = new DefaultDownloadIndex(databaseProvider);
assertEqual(downloadIndex.getDownload("http://www.test.com/manifest.mpd"), dashDownload);
assertEqual(downloadIndex.getDownload("http://www.test.com/manifest.m3u8"), hlsDownload);
assertEqual(downloadIndex.getDownload("http://www.test.com/video.ism/manifest"), ssDownload);
assertEqual(downloadIndex.getDownload("http://www.test.com/video.mp4"), progressiveDownload);
}
@Test
public void setStopReason_setReasonToNone() throws Exception { public void setStopReason_setReasonToNone() throws Exception {
String id = "id"; String id = "id";
DownloadBuilder downloadBuilder = DownloadBuilder downloadBuilder =
...@@ -210,10 +268,10 @@ public class DefaultDownloadIndexTest { ...@@ -210,10 +268,10 @@ public class DefaultDownloadIndexTest {
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
downloadIndex.setStopReason(Download.STOP_REASON_NONE); downloadIndex.setStopReason(STOP_REASON_NONE);
Download readDownload = downloadIndex.getDownload(id); Download readDownload = downloadIndex.getDownload(id);
Download expectedDownload = downloadBuilder.setStopReason(Download.STOP_REASON_NONE).build(); Download expectedDownload = downloadBuilder.setStopReason(STOP_REASON_NONE).build();
assertEqual(readDownload, expectedDownload); assertEqual(readDownload, expectedDownload);
} }
...@@ -223,7 +281,7 @@ public class DefaultDownloadIndexTest { ...@@ -223,7 +281,7 @@ public class DefaultDownloadIndexTest {
DownloadBuilder downloadBuilder = DownloadBuilder downloadBuilder =
new DownloadBuilder(id) new DownloadBuilder(id)
.setState(Download.STATE_FAILED) .setState(Download.STATE_FAILED)
.setFailureReason(Download.FAILURE_REASON_UNKNOWN); .setFailureReason(FAILURE_REASON_UNKNOWN);
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
int stopReason = 0x12345678; int stopReason = 0x12345678;
...@@ -238,7 +296,7 @@ public class DefaultDownloadIndexTest { ...@@ -238,7 +296,7 @@ public class DefaultDownloadIndexTest {
@Test @Test
public void setStopReason_notTerminalState_doesNotSetStopReason() throws Exception { public void setStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
String id = "id"; String id = "id";
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(Download.STATE_DOWNLOADING); DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(STATE_DOWNLOADING);
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
int notMetRequirements = 0x12345678; int notMetRequirements = 0x12345678;
...@@ -255,7 +313,7 @@ public class DefaultDownloadIndexTest { ...@@ -255,7 +313,7 @@ public class DefaultDownloadIndexTest {
DownloadBuilder downloadBuilder = DownloadBuilder downloadBuilder =
new DownloadBuilder(id) new DownloadBuilder(id)
.setState(Download.STATE_FAILED) .setState(Download.STATE_FAILED)
.setFailureReason(Download.FAILURE_REASON_UNKNOWN); .setFailureReason(FAILURE_REASON_UNKNOWN);
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
...@@ -263,7 +321,7 @@ public class DefaultDownloadIndexTest { ...@@ -263,7 +321,7 @@ public class DefaultDownloadIndexTest {
download = downloadIndex.getDownload(id); download = downloadIndex.getDownload(id);
assertThat(download.state).isEqualTo(Download.STATE_REMOVING); assertThat(download.state).isEqualTo(Download.STATE_REMOVING);
assertThat(download.failureReason).isEqualTo(Download.FAILURE_REASON_NONE); assertThat(download.failureReason).isEqualTo(FAILURE_REASON_NONE);
} }
@Test @Test
...@@ -274,10 +332,10 @@ public class DefaultDownloadIndexTest { ...@@ -274,10 +332,10 @@ public class DefaultDownloadIndexTest {
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
downloadIndex.setStopReason(id, Download.STOP_REASON_NONE); downloadIndex.setStopReason(id, STOP_REASON_NONE);
Download readDownload = downloadIndex.getDownload(id); Download readDownload = downloadIndex.getDownload(id);
Download expectedDownload = downloadBuilder.setStopReason(Download.STOP_REASON_NONE).build(); Download expectedDownload = downloadBuilder.setStopReason(STOP_REASON_NONE).build();
assertEqual(readDownload, expectedDownload); assertEqual(readDownload, expectedDownload);
} }
...@@ -287,7 +345,7 @@ public class DefaultDownloadIndexTest { ...@@ -287,7 +345,7 @@ public class DefaultDownloadIndexTest {
DownloadBuilder downloadBuilder = DownloadBuilder downloadBuilder =
new DownloadBuilder(id) new DownloadBuilder(id)
.setState(Download.STATE_FAILED) .setState(Download.STATE_FAILED)
.setFailureReason(Download.FAILURE_REASON_UNKNOWN); .setFailureReason(FAILURE_REASON_UNKNOWN);
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
int stopReason = 0x12345678; int stopReason = 0x12345678;
...@@ -302,7 +360,7 @@ public class DefaultDownloadIndexTest { ...@@ -302,7 +360,7 @@ public class DefaultDownloadIndexTest {
@Test @Test
public void setSingleDownloadStopReason_notTerminalState_doesNotSetStopReason() throws Exception { public void setSingleDownloadStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
String id = "id"; String id = "id";
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(Download.STATE_DOWNLOADING); DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(STATE_DOWNLOADING);
Download download = downloadBuilder.build(); Download download = downloadBuilder.build();
downloadIndex.putDownload(download); downloadIndex.putDownload(download);
int notMetRequirements = 0x12345678; int notMetRequirements = 0x12345678;
...@@ -324,4 +382,23 @@ public class DefaultDownloadIndexTest { ...@@ -324,4 +382,23 @@ public class DefaultDownloadIndexTest {
assertThat(download.getPercentDownloaded()).isEqualTo(that.getPercentDownloaded()); assertThat(download.getPercentDownloaded()).isEqualTo(that.getPercentDownloaded());
assertThat(download.getBytesDownloaded()).isEqualTo(that.getBytesDownloaded()); assertThat(download.getBytesDownloaded()).isEqualTo(that.getBytesDownloaded());
} }
private static Download createDownload(
String uri, String mimeType, List<StreamKey> streamKeys, @Nullable String customCacheKey) {
return new Download(
new DownloadRequest(
uri,
Uri.parse(uri),
mimeType,
streamKeys,
/* keySetId= */ null,
customCacheKey,
/* data= */ new byte[] {0, 1, 2, 3}),
/* state= */ STATE_STOPPED,
/* startTimeMs= */ 1,
/* updateTimeMs= */ 2,
/* contentLength= */ 3,
/* stopReason= */ 4,
/* failureReason= */ FAILURE_REASON_NONE);
}
} }
...@@ -43,10 +43,11 @@ public final class DefaultDownloaderFactoryTest { ...@@ -43,10 +43,11 @@ public final class DefaultDownloaderFactoryTest {
Downloader downloader = Downloader downloader =
factory.createDownloader( factory.createDownloader(
new DownloadRequest( new DownloadRequest(
"id", /* id= */ "id",
DownloadRequest.TYPE_PROGRESSIVE,
Uri.parse("https://www.test.com/download"), Uri.parse("https://www.test.com/download"),
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null)); /* data= */ null));
assertThat(downloader).isInstanceOf(ProgressiveDownloader.class); assertThat(downloader).isInstanceOf(ProgressiveDownloader.class);
......
...@@ -403,8 +403,8 @@ public class DownloadHelperTest { ...@@ -403,8 +403,8 @@ public class DownloadHelperTest {
DownloadRequest downloadRequest = downloadHelper.getDownloadRequest(data); DownloadRequest downloadRequest = downloadHelper.getDownloadRequest(data);
assertThat(downloadRequest.type).isEqualTo(DownloadRequest.TYPE_PROGRESSIVE);
assertThat(downloadRequest.uri).isEqualTo(testMediaItem.playbackProperties.uri); assertThat(downloadRequest.uri).isEqualTo(testMediaItem.playbackProperties.uri);
assertThat(downloadRequest.mimeType).isEqualTo(testMediaItem.playbackProperties.mimeType);
assertThat(downloadRequest.customCacheKey) assertThat(downloadRequest.customCacheKey)
.isEqualTo(testMediaItem.playbackProperties.customCacheKey); .isEqualTo(testMediaItem.playbackProperties.customCacheKey);
assertThat(downloadRequest.data).isEqualTo(data); assertThat(downloadRequest.data).isEqualTo(data);
......
...@@ -790,9 +790,10 @@ public class DownloadManagerTest { ...@@ -790,9 +790,10 @@ public class DownloadManagerTest {
private static DownloadRequest createDownloadRequest(String id, StreamKey... keys) { private static DownloadRequest createDownloadRequest(String id, StreamKey... keys) {
return new DownloadRequest( return new DownloadRequest(
id, id,
DownloadRequest.TYPE_DASH,
Uri.parse("http://abc.com/ " + id), Uri.parse("http://abc.com/ " + id),
/* mimeType= */ null,
Arrays.asList(keys), Arrays.asList(keys),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null); /* data= */ null);
} }
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
*/ */
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_DASH;
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_HLS;
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_PROGRESSIVE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
...@@ -48,44 +45,20 @@ public class DownloadRequestTest { ...@@ -48,44 +45,20 @@ public class DownloadRequestTest {
public void mergeRequests_withDifferentIds_fails() { public void mergeRequests_withDifferentIds_fails() {
DownloadRequest request1 = DownloadRequest request1 =
new DownloadRequest( new DownloadRequest(
"id1", /* id= */ "id1",
TYPE_DASH,
uri1, uri1,
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null); /* data= */ null);
DownloadRequest request2 = DownloadRequest request2 =
new DownloadRequest( new DownloadRequest(
"id2", /* id= */ "id2",
TYPE_DASH,
uri2, uri2,
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* customCacheKey= */ null, /* keySetId= */ null,
/* data= */ null);
try {
request1.copyWithMergedRequest(request2);
fail();
} catch (IllegalArgumentException e) {
// Expected.
}
}
@Test
public void mergeRequests_withDifferentTypes_fails() {
DownloadRequest request1 =
new DownloadRequest(
"id1",
TYPE_DASH,
uri1,
/* streamKeys= */ Collections.emptyList(),
/* customCacheKey= */ null,
/* data= */ null);
DownloadRequest request2 =
new DownloadRequest(
"id1",
TYPE_HLS,
uri1,
/* streamKeys= */ Collections.emptyList(),
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null); /* data= */ null);
try { try {
...@@ -135,33 +108,40 @@ public class DownloadRequestTest { ...@@ -135,33 +108,40 @@ public class DownloadRequestTest {
@Test @Test
public void mergeRequests_withDifferentFields() { public void mergeRequests_withDifferentFields() {
byte[] data1 = new byte[] {0, 1, 2}; byte[] keySetId1 = new byte[] {0, 1, 2};
byte[] data2 = new byte[] {3, 4, 5}; byte[] keySetId2 = new byte[] {3, 4, 5};
byte[] data1 = new byte[] {6, 7, 8};
byte[] data2 = new byte[] {9, 10, 11};
DownloadRequest request1 = DownloadRequest request1 =
new DownloadRequest( new DownloadRequest(
"id1", /* id= */ "id1",
TYPE_PROGRESSIVE,
uri1, uri1,
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
"key1", keySetId1,
/* data= */ data1); /* customCacheKey= */ "key1",
data1);
DownloadRequest request2 = DownloadRequest request2 =
new DownloadRequest( new DownloadRequest(
"id1", /* id= */ "id1",
TYPE_PROGRESSIVE,
uri2, uri2,
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
"key2", keySetId2,
/* data= */ data2); /* customCacheKey= */ "key2",
data2);
// uri, customCacheKey and data should be from the request being merged. // uri, keySetId, customCacheKey and data should be from the request being merged.
DownloadRequest mergedRequest = request1.copyWithMergedRequest(request2); DownloadRequest mergedRequest = request1.copyWithMergedRequest(request2);
assertThat(mergedRequest.uri).isEqualTo(uri2); assertThat(mergedRequest.uri).isEqualTo(uri2);
assertThat(mergedRequest.keySetId).isEqualTo(keySetId2);
assertThat(mergedRequest.customCacheKey).isEqualTo("key2"); assertThat(mergedRequest.customCacheKey).isEqualTo("key2");
assertThat(mergedRequest.data).isEqualTo(data2); assertThat(mergedRequest.data).isEqualTo(data2);
mergedRequest = request2.copyWithMergedRequest(request1); mergedRequest = request2.copyWithMergedRequest(request1);
assertThat(mergedRequest.uri).isEqualTo(uri1); assertThat(mergedRequest.uri).isEqualTo(uri1);
assertThat(mergedRequest.keySetId).isEqualTo(keySetId1);
assertThat(mergedRequest.customCacheKey).isEqualTo("key1"); assertThat(mergedRequest.customCacheKey).isEqualTo("key1");
assertThat(mergedRequest.data).isEqualTo(data1); assertThat(mergedRequest.data).isEqualTo(data1);
} }
...@@ -173,12 +153,13 @@ public class DownloadRequestTest { ...@@ -173,12 +153,13 @@ public class DownloadRequestTest {
streamKeys.add(new StreamKey(4, 5, 6)); streamKeys.add(new StreamKey(4, 5, 6));
DownloadRequest requestToParcel = DownloadRequest requestToParcel =
new DownloadRequest( new DownloadRequest(
"id", /* id= */ "id",
"type",
Uri.parse("https://abc.def/ghi"), Uri.parse("https://abc.def/ghi"),
/* mimeType= */ null,
streamKeys, streamKeys,
"key", /* keySetId= */ new byte[] {1, 2, 3, 4, 5},
new byte[] {1, 2, 3, 4, 5}); /* customCacheKey= */ "key",
/* data= */ new byte[] {1, 2, 3, 4, 5});
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
requestToParcel.writeToParcel(parcel, 0); requestToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0); parcel.setDataPosition(0);
...@@ -232,11 +213,18 @@ public class DownloadRequestTest { ...@@ -232,11 +213,18 @@ public class DownloadRequestTest {
private static void assertEqual(DownloadRequest request1, DownloadRequest request2) { private static void assertEqual(DownloadRequest request1, DownloadRequest request2) {
assertThat(request1).isEqualTo(request2); assertThat(request1).isEqualTo(request2);
assertThat(request2).isEqualTo(request1); assertThat(request2).isEqualTo(request1);
assertThat(request1.hashCode()).isEqualTo(request2.hashCode());
} }
private static DownloadRequest createRequest(Uri uri, StreamKey... keys) { private static DownloadRequest createRequest(Uri uri, StreamKey... keys) {
return new DownloadRequest( return new DownloadRequest(
uri.toString(), TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, /* data= */ null); uri.toString(),
uri,
/* mimeType= */ null,
toList(keys),
/* keySetId= */ null,
/* customCacheKey= */ null,
/* data= */ null);
} }
private static List<StreamKey> toList(StreamKey... keys) { private static List<StreamKey> toList(StreamKey... keys) {
......
...@@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache; ...@@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
...@@ -93,9 +94,10 @@ public class DashDownloaderTest { ...@@ -93,9 +94,10 @@ public class DashDownloaderTest {
factory.createDownloader( factory.createDownloader(
new DownloadRequest( new DownloadRequest(
"id", "id",
DownloadRequest.TYPE_DASH,
Uri.parse("https://www.test.com/download"), Uri.parse("https://www.test.com/download"),
MimeTypes.APPLICATION_MPD,
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)), Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null)); /* data= */ null));
assertThat(downloader).isInstanceOf(DashDownloader.class); assertThat(downloader).isInstanceOf(DashDownloader.class);
......
...@@ -42,6 +42,7 @@ import com.google.android.exoplayer2.upstream.DataSource.Factory; ...@@ -42,6 +42,7 @@ import com.google.android.exoplayer2.upstream.DataSource.Factory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -224,9 +225,10 @@ public class DownloadManagerDashTest { ...@@ -224,9 +225,10 @@ public class DownloadManagerDashTest {
Collections.addAll(keysList, keys); Collections.addAll(keysList, keys);
return new DownloadRequest( return new DownloadRequest(
TEST_ID, TEST_ID,
DownloadRequest.TYPE_DASH,
TEST_MPD_URI, TEST_MPD_URI,
MimeTypes.APPLICATION_MPD,
keysList, keysList,
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
null); null);
} }
......
...@@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource; ...@@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
...@@ -209,9 +210,10 @@ public class DownloadServiceDashTest { ...@@ -209,9 +210,10 @@ public class DownloadServiceDashTest {
DownloadRequest action = DownloadRequest action =
new DownloadRequest( new DownloadRequest(
TEST_ID, TEST_ID,
DownloadRequest.TYPE_DASH,
TEST_MPD_URI, TEST_MPD_URI,
MimeTypes.APPLICATION_MPD,
keysList, keysList,
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
null); null);
testThread.runOnMainThread( testThread.runOnMainThread(
......
...@@ -53,6 +53,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache; ...@@ -53,6 +53,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor; import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -111,9 +112,10 @@ public class HlsDownloaderTest { ...@@ -111,9 +112,10 @@ public class HlsDownloaderTest {
factory.createDownloader( factory.createDownloader(
new DownloadRequest( new DownloadRequest(
"id", "id",
DownloadRequest.TYPE_HLS,
Uri.parse("https://www.test.com/download"), Uri.parse("https://www.test.com/download"),
MimeTypes.APPLICATION_M3U8,
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)), Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null)); /* data= */ null));
assertThat(downloader).isInstanceOf(HlsDownloader.class); assertThat(downloader).isInstanceOf(HlsDownloader.class);
......
...@@ -27,6 +27,7 @@ import com.google.android.exoplayer2.offline.StreamKey; ...@@ -27,6 +27,7 @@ import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -49,9 +50,10 @@ public final class SsDownloaderTest { ...@@ -49,9 +50,10 @@ public final class SsDownloaderTest {
factory.createDownloader( factory.createDownloader(
new DownloadRequest( new DownloadRequest(
"id", "id",
DownloadRequest.TYPE_SS,
Uri.parse("https://www.test.com/download"), Uri.parse("https://www.test.com/download"),
MimeTypes.APPLICATION_SS,
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)), Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
/* keySetId= */ null,
/* customCacheKey= */ null, /* customCacheKey= */ null,
/* data= */ null)); /* data= */ null));
assertThat(downloader).isInstanceOf(SsDownloader.class); assertThat(downloader).isInstanceOf(SsDownloader.class);
......
...@@ -38,9 +38,10 @@ public final class DownloadBuilder { ...@@ -38,9 +38,10 @@ public final class DownloadBuilder {
private final DownloadProgress progress; private final DownloadProgress progress;
private String id; private String id;
private String type;
private Uri uri; private Uri uri;
@Nullable private String mimeType;
private List<StreamKey> streamKeys; private List<StreamKey> streamKeys;
private byte[] keySetId;
@Nullable private String cacheKey; @Nullable private String cacheKey;
private byte[] customMetadata; private byte[] customMetadata;
...@@ -52,18 +53,19 @@ public final class DownloadBuilder { ...@@ -52,18 +53,19 @@ public final class DownloadBuilder {
private int failureReason; private int failureReason;
/** /**
* Creates a download builder for "uri" with type "type" and no stream keys. * Creates a download builder for "uri" and no stream keys.
* *
* @param id The unique content identifier for the download. * @param id The unique content identifier for the download.
*/ */
public DownloadBuilder(String id) { public DownloadBuilder(String id) {
this( this(
id, id,
"type",
Uri.parse("uri"), Uri.parse("uri"),
/* mimeType= */ null,
/* streamKeys= */ Collections.emptyList(), /* streamKeys= */ Collections.emptyList(),
/* keySetId= */ new byte[0],
/* cacheKey= */ null, /* cacheKey= */ null,
new byte[0]); /* customMetadata= */ new byte[0]);
} }
/** /**
...@@ -74,9 +76,10 @@ public final class DownloadBuilder { ...@@ -74,9 +76,10 @@ public final class DownloadBuilder {
public DownloadBuilder(DownloadRequest request) { public DownloadBuilder(DownloadRequest request) {
this( this(
request.id, request.id,
request.type,
request.uri, request.uri,
request.mimeType,
request.streamKeys, request.streamKeys,
request.keySetId,
request.customCacheKey, request.customCacheKey,
request.data); request.data);
} }
...@@ -84,15 +87,17 @@ public final class DownloadBuilder { ...@@ -84,15 +87,17 @@ public final class DownloadBuilder {
/** Creates a download builder. */ /** Creates a download builder. */
private DownloadBuilder( private DownloadBuilder(
String id, String id,
String type,
Uri uri, Uri uri,
@Nullable String mimeType,
List<StreamKey> streamKeys, List<StreamKey> streamKeys,
byte[] keySetId,
@Nullable String cacheKey, @Nullable String cacheKey,
byte[] customMetadata) { byte[] customMetadata) {
this.id = id; this.id = id;
this.type = type;
this.uri = uri; this.uri = uri;
this.mimeType = mimeType;
this.streamKeys = streamKeys; this.streamKeys = streamKeys;
this.keySetId = keySetId;
this.cacheKey = cacheKey; this.cacheKey = cacheKey;
this.customMetadata = customMetadata; this.customMetadata = customMetadata;
this.state = Download.STATE_QUEUED; this.state = Download.STATE_QUEUED;
...@@ -101,12 +106,6 @@ public final class DownloadBuilder { ...@@ -101,12 +106,6 @@ public final class DownloadBuilder {
this.progress = new DownloadProgress(); this.progress = new DownloadProgress();
} }
/** @see DownloadRequest#type */
public DownloadBuilder setType(String type) {
this.type = type;
return this;
}
/** @see DownloadRequest#uri */ /** @see DownloadRequest#uri */
public DownloadBuilder setUri(String uri) { public DownloadBuilder setUri(String uri) {
this.uri = Uri.parse(uri); this.uri = Uri.parse(uri);
...@@ -119,6 +118,18 @@ public final class DownloadBuilder { ...@@ -119,6 +118,18 @@ public final class DownloadBuilder {
return this; return this;
} }
/** @see DownloadRequest#mimeType */
public DownloadBuilder setMimeType(String mimeType) {
this.mimeType = mimeType;
return this;
}
/** @see DownloadRequest#keySetId */
public DownloadBuilder setKeySetId(byte[] keySetId) {
this.keySetId = keySetId;
return this;
}
/** @see DownloadRequest#customCacheKey */ /** @see DownloadRequest#customCacheKey */
public DownloadBuilder setCacheKey(@Nullable String cacheKey) { public DownloadBuilder setCacheKey(@Nullable String cacheKey) {
this.cacheKey = cacheKey; this.cacheKey = cacheKey;
...@@ -187,7 +198,7 @@ public final class DownloadBuilder { ...@@ -187,7 +198,7 @@ public final class DownloadBuilder {
public Download build() { public Download build() {
DownloadRequest request = DownloadRequest request =
new DownloadRequest(id, type, uri, streamKeys, cacheKey, customMetadata); new DownloadRequest(id, uri, mimeType, streamKeys, keySetId, cacheKey, customMetadata);
return new Download( return new Download(
request, request,
state, state,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment