Add DownloadIndex and DefaultDownloadIndex
DownloadIndex will be used to store and query DownloadStates. PiperOrigin-RevId: 228673766
This commit is contained in:
parent
637b52ae0e
commit
92bec21c03
@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A {@link DownloadIndex} which uses SQLite to persist {@link DownloadState}s.
|
||||
*
|
||||
* <p class="caution">Database access may take a long time, do not call methods of this class from
|
||||
* the application main thread.
|
||||
*/
|
||||
public final class DefaultDownloadIndex implements DownloadIndex {
|
||||
|
||||
/** Provides {@link SQLiteDatabase} instances. */
|
||||
public interface DatabaseProvider {
|
||||
/** Closes any open database object. */
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Creates and/or opens a database that will be used for reading and writing.
|
||||
*
|
||||
* <p>Once opened successfully, the database is cached, so you can call this method every time
|
||||
* you need to write to the database. (Make sure to call {@link #close} when you no longer need
|
||||
* the database.) Errors such as bad permissions or a full disk may cause this method to fail,
|
||||
* but future attempts may succeed if the problem is fixed.
|
||||
*
|
||||
* @throws SQLiteException If the database cannot be opened for writing.
|
||||
* @return A read/write database object valid until {@link #close} is called.
|
||||
*/
|
||||
SQLiteDatabase getWritableDatabase();
|
||||
|
||||
/**
|
||||
* Creates and/or opens a database. This will be the same object returned by {@link
|
||||
* #getWritableDatabase} unless some problem, such as a full disk, requires the database to be
|
||||
* opened read-only. In that case, a read-only database object will be returned. If the problem
|
||||
* is fixed, a future call to {@link #getWritableDatabase} may succeed, in which case the
|
||||
* read-only database object will be closed and the read/write object will be returned in the
|
||||
* future.
|
||||
*
|
||||
* <p>Once opened successfully, the database should be cached. When the database is no longer
|
||||
* needed, {@link #close} will be called.
|
||||
*
|
||||
* @throws SQLiteException If the database cannot be opened.
|
||||
* @return A database object valid until {@link #getWritableDatabase} or {@link #close} is
|
||||
* called.
|
||||
*/
|
||||
SQLiteDatabase getReadableDatabase();
|
||||
}
|
||||
|
||||
private static final String DATABASE_NAME = "exoplayer_internal.db";
|
||||
|
||||
private final DatabaseProvider databaseProvider;
|
||||
@Nullable private DownloadStateTable downloadStateTable;
|
||||
|
||||
/**
|
||||
* Creates a DefaultDownloadIndex which stores the {@link DownloadState}s on a SQLite database.
|
||||
*
|
||||
* @param context A Context.
|
||||
*/
|
||||
public DefaultDownloadIndex(Context context) {
|
||||
this(new DefaultDatabaseProvider(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DefaultDownloadIndex which stores the {@link DownloadState}s on a SQLite database
|
||||
* provided by {@code databaseProvider}.
|
||||
*
|
||||
* @param databaseProvider A DatabaseProvider which provides the database which will be used to
|
||||
* store DownloadStatus table.
|
||||
*/
|
||||
public DefaultDownloadIndex(DatabaseProvider databaseProvider) {
|
||||
this.databaseProvider = databaseProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DownloadState getDownloadState(String id) {
|
||||
return getDownloadStateTable().get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadStateCursor getDownloadStates(@DownloadState.State int... states) {
|
||||
return getDownloadStateTable().get(states);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putDownloadState(DownloadState downloadState) {
|
||||
getDownloadStateTable().replace(downloadState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDownloadState(String id) {
|
||||
getDownloadStateTable().delete(id);
|
||||
}
|
||||
|
||||
private DownloadStateTable getDownloadStateTable() {
|
||||
if (downloadStateTable == null) {
|
||||
downloadStateTable = new DownloadStateTable(databaseProvider);
|
||||
}
|
||||
return downloadStateTable;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ static boolean doesTableExist(DatabaseProvider databaseProvider, String tableName) {
|
||||
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||
long count =
|
||||
DatabaseUtils.queryNumEntries(
|
||||
readableDatabase, "sqlite_master", "tbl_name = ?", new String[] {tableName});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
private static final class DownloadStateCursorImpl implements DownloadStateCursor {
|
||||
|
||||
private final Cursor cursor;
|
||||
|
||||
private DownloadStateCursorImpl(Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadState getDownloadState() {
|
||||
return DownloadStateTable.getDownloadState(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return cursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
return cursor.getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToPosition(int position) {
|
||||
return cursor.moveToPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return cursor.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ static final class DownloadStateTable {
|
||||
@VisibleForTesting /* package */ static final String TABLE_NAME = "ExoPlayerDownloadStates";
|
||||
@VisibleForTesting /* package */ static final int TABLE_VERSION = 1;
|
||||
|
||||
private static final String COLUMN_ID = "id";
|
||||
private static final String COLUMN_TYPE = "title";
|
||||
private static final String COLUMN_URI = "subtitle";
|
||||
private static final String COLUMN_CACHE_KEY = "cache_key";
|
||||
private static final String COLUMN_STATE = "state";
|
||||
private static final String COLUMN_DOWNLOAD_PERCENTAGE = "download_percentage";
|
||||
private static final String COLUMN_DOWNLOADED_BYTES = "downloaded_bytes";
|
||||
private static final String COLUMN_TOTAL_BYTES = "total_bytes";
|
||||
private static final String COLUMN_FAILURE_REASON = "failure_reason";
|
||||
private static final String COLUMN_STOP_FLAGS = "stop_flags";
|
||||
private static final String COLUMN_START_TIME_MS = "start_time_ms";
|
||||
private static final String COLUMN_UPDATE_TIME_MS = "update_time_ms";
|
||||
private static final String COLUMN_STREAM_KEYS = "stream_keys";
|
||||
private static final String COLUMN_CUSTOM_METADATA = "custom_metadata";
|
||||
|
||||
private static final int COLUMN_INDEX_ID = 0;
|
||||
private static final int COLUMN_INDEX_TYPE = 1;
|
||||
private static final int COLUMN_INDEX_URI = 2;
|
||||
private static final int COLUMN_INDEX_CACHE_KEY = 3;
|
||||
private static final int COLUMN_INDEX_STATE = 4;
|
||||
private static final int COLUMN_INDEX_DOWNLOAD_PERCENTAGE = 5;
|
||||
private static final int COLUMN_INDEX_DOWNLOADED_BYTES = 6;
|
||||
private static final int COLUMN_INDEX_TOTAL_BYTES = 7;
|
||||
private static final int COLUMN_INDEX_FAILURE_REASON = 8;
|
||||
private static final int COLUMN_INDEX_STOP_FLAGS = 9;
|
||||
private static final int COLUMN_INDEX_START_TIME_MS = 10;
|
||||
private static final int COLUMN_INDEX_UPDATE_TIME_MS = 11;
|
||||
private static final int COLUMN_INDEX_STREAM_KEYS = 12;
|
||||
private static final int COLUMN_INDEX_CUSTOM_METADATA = 13;
|
||||
|
||||
private static final String COLUMN_SELECTION_ID = COLUMN_ID + " = ?";
|
||||
|
||||
private static final String[] COLUMNS =
|
||||
new String[] {
|
||||
COLUMN_ID,
|
||||
COLUMN_TYPE,
|
||||
COLUMN_URI,
|
||||
COLUMN_CACHE_KEY,
|
||||
COLUMN_STATE,
|
||||
COLUMN_DOWNLOAD_PERCENTAGE,
|
||||
COLUMN_DOWNLOADED_BYTES,
|
||||
COLUMN_TOTAL_BYTES,
|
||||
COLUMN_FAILURE_REASON,
|
||||
COLUMN_STOP_FLAGS,
|
||||
COLUMN_START_TIME_MS,
|
||||
COLUMN_UPDATE_TIME_MS,
|
||||
COLUMN_STREAM_KEYS,
|
||||
COLUMN_CUSTOM_METADATA
|
||||
};
|
||||
|
||||
private static final String SQL_DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
private static final String SQL_CREATE_TABLE =
|
||||
"CREATE TABLE IF NOT EXISTS "
|
||||
+ TABLE_NAME
|
||||
+ " ("
|
||||
+ COLUMN_ID
|
||||
+ " TEXT PRIMARY KEY NOT NULL,"
|
||||
+ COLUMN_TYPE
|
||||
+ " TEXT NOT NULL,"
|
||||
+ COLUMN_URI
|
||||
+ " TEXT NOT NULL,"
|
||||
+ COLUMN_CACHE_KEY
|
||||
+ " TEXT,"
|
||||
+ COLUMN_STATE
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_DOWNLOAD_PERCENTAGE
|
||||
+ " REAL NOT NULL,"
|
||||
+ COLUMN_DOWNLOADED_BYTES
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_TOTAL_BYTES
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_FAILURE_REASON
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_STOP_FLAGS
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_START_TIME_MS
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_UPDATE_TIME_MS
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_STREAM_KEYS
|
||||
+ " TEXT NOT NULL,"
|
||||
+ COLUMN_CUSTOM_METADATA
|
||||
+ " BLOB NOT NULL)";
|
||||
|
||||
private final DatabaseProvider databaseProvider;
|
||||
|
||||
public DownloadStateTable(DatabaseProvider databaseProvider) {
|
||||
this.databaseProvider = databaseProvider;
|
||||
VersionTable versionTable = new VersionTable(databaseProvider);
|
||||
int version = versionTable.getVersion(VersionTable.FEATURE_OFFLINE);
|
||||
if (!doesTableExist(databaseProvider, TABLE_NAME)
|
||||
|| version == 0
|
||||
|| version > TABLE_VERSION) {
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
writableDatabase.execSQL(SQL_DROP_TABLE);
|
||||
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||
versionTable.setVersion(VersionTable.FEATURE_OFFLINE, TABLE_VERSION);
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
writableDatabase.endTransaction();
|
||||
}
|
||||
} else if (version < TABLE_VERSION) {
|
||||
// There is no previous version currently.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public void replace(DownloadState downloadState) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_ID, downloadState.id);
|
||||
values.put(COLUMN_TYPE, downloadState.type);
|
||||
values.put(COLUMN_URI, downloadState.uri.toString());
|
||||
values.put(COLUMN_CACHE_KEY, downloadState.cacheKey);
|
||||
values.put(COLUMN_STATE, downloadState.state);
|
||||
values.put(COLUMN_DOWNLOAD_PERCENTAGE, downloadState.downloadPercentage);
|
||||
values.put(COLUMN_DOWNLOADED_BYTES, downloadState.downloadedBytes);
|
||||
values.put(COLUMN_TOTAL_BYTES, downloadState.totalBytes);
|
||||
values.put(COLUMN_FAILURE_REASON, downloadState.failureReason);
|
||||
values.put(COLUMN_STOP_FLAGS, downloadState.stopFlags);
|
||||
values.put(COLUMN_START_TIME_MS, downloadState.startTimeMs);
|
||||
values.put(COLUMN_UPDATE_TIME_MS, downloadState.updateTimeMs);
|
||||
values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(downloadState.streamKeys));
|
||||
values.put(COLUMN_CUSTOM_METADATA, downloadState.customMetadata);
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DownloadState get(String id) {
|
||||
String[] selectionArgs = {id};
|
||||
try (Cursor cursor = query(COLUMN_SELECTION_ID, selectionArgs)) {
|
||||
if (cursor.getCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
cursor.moveToNext();
|
||||
DownloadState downloadState = getDownloadState(cursor);
|
||||
Assertions.checkState(id.equals(downloadState.id));
|
||||
return downloadState;
|
||||
}
|
||||
}
|
||||
|
||||
public DownloadStateCursor get(@DownloadState.State int... states) {
|
||||
String selection = null;
|
||||
if (states.length > 0) {
|
||||
StringBuilder selectionBuilder = new StringBuilder();
|
||||
selectionBuilder.append(COLUMN_STATE).append(" IN (");
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (i > 0) {
|
||||
selectionBuilder.append(',');
|
||||
}
|
||||
selectionBuilder.append(states[i]);
|
||||
}
|
||||
selectionBuilder.append(')');
|
||||
selection = selectionBuilder.toString();
|
||||
}
|
||||
Cursor cursor = query(selection, /* selectionArgs= */ null);
|
||||
return new DownloadStateCursorImpl(cursor);
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
String[] selectionArgs = {id};
|
||||
databaseProvider.getWritableDatabase().delete(TABLE_NAME, COLUMN_SELECTION_ID, selectionArgs);
|
||||
}
|
||||
|
||||
private Cursor query(@Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
String sortOrder = COLUMN_START_TIME_MS + " ASC";
|
||||
return databaseProvider
|
||||
.getReadableDatabase()
|
||||
.query(
|
||||
TABLE_NAME,
|
||||
COLUMNS,
|
||||
selection,
|
||||
selectionArgs,
|
||||
/* groupBy= */ null,
|
||||
/* having= */ null,
|
||||
sortOrder);
|
||||
}
|
||||
|
||||
private static DownloadState getDownloadState(Cursor cursor) {
|
||||
return new DownloadState(
|
||||
cursor.getString(COLUMN_INDEX_ID),
|
||||
cursor.getString(COLUMN_INDEX_TYPE),
|
||||
Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
|
||||
cursor.getString(COLUMN_INDEX_CACHE_KEY),
|
||||
cursor.getInt(COLUMN_INDEX_STATE),
|
||||
cursor.getFloat(COLUMN_INDEX_DOWNLOAD_PERCENTAGE),
|
||||
cursor.getLong(COLUMN_INDEX_DOWNLOADED_BYTES),
|
||||
cursor.getLong(COLUMN_INDEX_TOTAL_BYTES),
|
||||
cursor.getInt(COLUMN_INDEX_FAILURE_REASON),
|
||||
cursor.getInt(COLUMN_INDEX_STOP_FLAGS),
|
||||
cursor.getLong(COLUMN_INDEX_START_TIME_MS),
|
||||
cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS),
|
||||
decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)),
|
||||
cursor.getBlob(COLUMN_INDEX_CUSTOM_METADATA));
|
||||
}
|
||||
|
||||
private static String encodeStreamKeys(StreamKey[] streamKeys) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (StreamKey streamKey : streamKeys) {
|
||||
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 StreamKey[] decodeStreamKeys(String encodedStreamKeys) {
|
||||
if (encodedStreamKeys.isEmpty()) {
|
||||
return new StreamKey[0];
|
||||
}
|
||||
String[] streamKeysStrings = Util.split(encodedStreamKeys, ",");
|
||||
int streamKeysCount = streamKeysStrings.length;
|
||||
StreamKey[] streamKeys = new StreamKey[streamKeysCount];
|
||||
for (int i = 0; i < streamKeysCount; i++) {
|
||||
String[] indices = Util.split(streamKeysStrings[i], "\\.");
|
||||
Assertions.checkState(indices.length == 3);
|
||||
streamKeys[i] =
|
||||
new StreamKey(
|
||||
Integer.parseInt(indices[0]),
|
||||
Integer.parseInt(indices[1]),
|
||||
Integer.parseInt(indices[2]));
|
||||
}
|
||||
return streamKeys;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ static final class VersionTable {
|
||||
private static final String TABLE_NAME = "ExoPlayerVersions";
|
||||
|
||||
private static final String COLUMN_FEATURE = "feature";
|
||||
private static final String COLUMN_VERSION = "version";
|
||||
|
||||
private static final String SQL_CREATE_TABLE =
|
||||
"CREATE TABLE IF NOT EXISTS "
|
||||
+ TABLE_NAME
|
||||
+ " ("
|
||||
+ COLUMN_FEATURE
|
||||
+ " INTEGER PRIMARY KEY NOT NULL,"
|
||||
+ COLUMN_VERSION
|
||||
+ " INTEGER NOT NULL)";
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({FEATURE_OFFLINE, FEATURE_CACHE})
|
||||
private @interface Feature {}
|
||||
|
||||
public static final int FEATURE_OFFLINE = 0;
|
||||
public static final int FEATURE_CACHE = 1;
|
||||
|
||||
private final DatabaseProvider databaseProvider;
|
||||
|
||||
public VersionTable(DatabaseProvider databaseProvider) {
|
||||
this.databaseProvider = databaseProvider;
|
||||
if (!doesTableExist(databaseProvider, TABLE_NAME)) {
|
||||
databaseProvider.getWritableDatabase().execSQL(SQL_CREATE_TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVersion(@Feature int feature, int version) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_FEATURE, feature);
|
||||
values.put(COLUMN_VERSION, version);
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||
}
|
||||
|
||||
public int getVersion(@Feature int feature) {
|
||||
String selection = COLUMN_FEATURE + " = ?";
|
||||
String[] selectionArgs = {Integer.toString(feature)};
|
||||
try (Cursor cursor =
|
||||
databaseProvider
|
||||
.getReadableDatabase()
|
||||
.query(
|
||||
TABLE_NAME,
|
||||
new String[] {COLUMN_VERSION},
|
||||
selection,
|
||||
selectionArgs,
|
||||
/* groupBy= */ null,
|
||||
/* having= */ null,
|
||||
/* orderBy= */ null)) {
|
||||
if (cursor.getCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
cursor.moveToNext();
|
||||
return cursor.getInt(/* COLUMN_VERSION index */ 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DefaultDatabaseProvider extends SQLiteOpenHelper
|
||||
implements DatabaseProvider {
|
||||
public DefaultDatabaseProvider(Context context) {
|
||||
super(context, DATABASE_NAME, /* factory= */ null, /* version= */ 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
// Table creation is done in DownloadStateTable constructor.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Upgrade is handled in DownloadStateTable constructor.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// TODO: Wipe the database.
|
||||
super.onDowngrade(db, oldVersion, newVersion);
|
||||
}
|
||||
|
||||
// DatabaseProvider implementation.
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteDatabase getWritableDatabase() {
|
||||
return super.getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLiteDatabase getReadableDatabase() {
|
||||
return super.getReadableDatabase();
|
||||
}
|
||||
}
|
||||
}
|
@ -156,7 +156,7 @@ public final class DownloadAction {
|
||||
ArrayList<StreamKey> mutableKeys = new ArrayList<>(keys);
|
||||
Collections.sort(mutableKeys);
|
||||
this.keys = Collections.unmodifiableList(mutableKeys);
|
||||
this.data = data != null ? data : Util.EMPTY_BYTE_ARRAY;
|
||||
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/** Persists {@link DownloadState}s. */
|
||||
interface DownloadIndex {
|
||||
/** Releases the used resources. */
|
||||
void release();
|
||||
|
||||
/**
|
||||
* Returns the {@link DownloadState} with the given {@code id}, or null.
|
||||
*
|
||||
* @param id ID of a {@link DownloadState}.
|
||||
* @return The {@link DownloadState} with the given {@code id}, or null if a download state with
|
||||
* this id doesn't exist.
|
||||
*/
|
||||
@Nullable
|
||||
DownloadState getDownloadState(String id);
|
||||
|
||||
/**
|
||||
* Returns a {@link DownloadStateCursor} to {@link DownloadState}s with the given {@code states}.
|
||||
*
|
||||
* @param states Returns only the {@link DownloadState}s with this states. If empty, returns all.
|
||||
* @return A cursor to {@link DownloadState}s with the given {@code states}.
|
||||
*/
|
||||
DownloadStateCursor getDownloadStates(@DownloadState.State int... states);
|
||||
|
||||
/**
|
||||
* Adds or replaces a {@link DownloadState}.
|
||||
*
|
||||
* @param downloadState The {@link DownloadState} to be added.
|
||||
*/
|
||||
void putDownloadState(DownloadState downloadState);
|
||||
|
||||
/** Removes the {@link DownloadState} with the given {@code id}. */
|
||||
void removeDownloadState(String id);
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
/** Provides random read-write access to the result set returned by a database query. */
|
||||
interface DownloadStateCursor {
|
||||
|
||||
/** Returns the DownloadState at the current position. */
|
||||
DownloadState getDownloadState();
|
||||
|
||||
/** Returns the numbers of DownloadStates in the cursor. */
|
||||
int getCount();
|
||||
|
||||
/**
|
||||
* Returns the current position of the cursor in the DownloadState set. The value is zero-based.
|
||||
* When the DownloadState set is first returned the cursor will be at positon -1, which is before
|
||||
* the first DownloadState. After the last DownloadState is returned another call to next() will
|
||||
* leave the cursor past the last entry, at a position of count().
|
||||
*
|
||||
* @return the current cursor position.
|
||||
*/
|
||||
int getPosition();
|
||||
|
||||
/**
|
||||
* Move the cursor to an absolute position. The valid range of values is -1 <= position <=
|
||||
* count.
|
||||
*
|
||||
* <p>This method will return true if the request destination was reachable, otherwise, it returns
|
||||
* false.
|
||||
*
|
||||
* @param position the zero-based position to move to.
|
||||
* @return whether the requested move fully succeeded.
|
||||
*/
|
||||
boolean moveToPosition(int position);
|
||||
|
||||
/**
|
||||
* Move the cursor to the first DownloadState.
|
||||
*
|
||||
* <p>This method will return false if the cursor is empty.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
default boolean moveToFirst() {
|
||||
return moveToPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to the last DownloadState.
|
||||
*
|
||||
* <p>This method will return false if the cursor is empty.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
default boolean moveToLast() {
|
||||
return moveToPosition(getCount() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to the next DownloadState.
|
||||
*
|
||||
* <p>This method will return false if the cursor is already past the last entry in the result
|
||||
* set.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
default boolean moveToNext() {
|
||||
return moveToPosition(getPosition() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the cursor to the previous DownloadState.
|
||||
*
|
||||
* <p>This method will return false if the cursor is already before the first entry in the result
|
||||
* set.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
default boolean moveToPrevious() {
|
||||
return moveToPosition(getPosition() - 1);
|
||||
}
|
||||
|
||||
/** Returns whether the cursor is pointing to the first DownloadState. */
|
||||
default boolean isFirst() {
|
||||
return getPosition() == 0 && getCount() != 0;
|
||||
}
|
||||
|
||||
/** Returns whether the cursor is pointing to the last DownloadState. */
|
||||
default boolean isLast() {
|
||||
int count = getCount();
|
||||
return getPosition() == (count - 1) && count != 0;
|
||||
}
|
||||
|
||||
/** Returns whether the cursor is pointing to the position before the first DownloadState. */
|
||||
default boolean isBeforeFirst() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return getPosition() == -1;
|
||||
}
|
||||
|
||||
/** Returns whether the cursor is pointing to the position after the last DownloadState. */
|
||||
default boolean isAfterLast() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return getPosition() == getCount();
|
||||
}
|
||||
|
||||
/** Closes the Cursor, releasing all of its resources and making it completely invalid. */
|
||||
void close();
|
||||
|
||||
/** Returns whether the cursor is closed */
|
||||
boolean isClosed();
|
||||
}
|
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import static com.google.android.exoplayer2.offline.DefaultDownloadIndex.VersionTable.FEATURE_CACHE;
|
||||
import static com.google.android.exoplayer2.offline.DefaultDownloadIndex.VersionTable.FEATURE_OFFLINE;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.util.Arrays;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
/** Unit tests for {@link DefaultDownloadIndex}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class DefaultDownloadIndexTest {
|
||||
|
||||
private DefaultDownloadIndex downloadIndex;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
downloadIndex = new DefaultDownloadIndex(RuntimeEnvironment.application);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
downloadIndex.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDownloadState_nonExistingId_returnsNull() {
|
||||
assertThat(downloadIndex.getDownloadState("non existing id")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addAndGetDownloadState_nonExistingId_returnsTheSameDownloadState() {
|
||||
String id = "id";
|
||||
DownloadState downloadState = new DownloadStateBuilder(id).build();
|
||||
|
||||
downloadIndex.putDownloadState(downloadState);
|
||||
DownloadState readDownloadState = downloadIndex.getDownloadState(id);
|
||||
|
||||
assertEqual(readDownloadState, downloadState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addAndGetDownloadState_existingId_returnsUpdatedDownloadState() {
|
||||
String id = "id";
|
||||
DownloadStateBuilder downloadStateBuilder = new DownloadStateBuilder(id);
|
||||
downloadIndex.putDownloadState(downloadStateBuilder.build());
|
||||
|
||||
DownloadState downloadState =
|
||||
downloadStateBuilder
|
||||
.setType("different type")
|
||||
.setUri("different uri")
|
||||
.setCacheKey("different cacheKey")
|
||||
.setState(DownloadState.STATE_FAILED)
|
||||
.setDownloadPercentage(50)
|
||||
.setDownloadedBytes(200)
|
||||
.setTotalBytes(400)
|
||||
.setFailureReason(DownloadState.FAILURE_REASON_UNKNOWN)
|
||||
.setStopFlags(DownloadState.STOP_FLAG_STOPPED)
|
||||
.setStartTimeMs(10)
|
||||
.setUpdateTimeMs(20)
|
||||
.setStreamKeys(
|
||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5))
|
||||
.setCustomMetadata(new byte[] {0, 1, 2, 3})
|
||||
.build();
|
||||
downloadIndex.putDownloadState(downloadState);
|
||||
DownloadState readDownloadState = downloadIndex.getDownloadState(id);
|
||||
|
||||
assertThat(readDownloadState).isNotNull();
|
||||
assertEqual(readDownloadState, downloadState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void releaseAndRecreateDownloadIndex_returnsTheSameDownloadState() {
|
||||
String id = "id";
|
||||
DownloadState downloadState = new DownloadStateBuilder(id).build();
|
||||
|
||||
downloadIndex.putDownloadState(downloadState);
|
||||
downloadIndex.release();
|
||||
downloadIndex = new DefaultDownloadIndex(RuntimeEnvironment.application);
|
||||
DownloadState readDownloadState = downloadIndex.getDownloadState(id);
|
||||
|
||||
assertThat(readDownloadState).isNotNull();
|
||||
assertEqual(readDownloadState, downloadState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customDatabaseProvider_getDownloadStateReturnsNull() {
|
||||
String id = "id";
|
||||
DownloadState downloadState = new DownloadStateBuilder(id).build();
|
||||
|
||||
downloadIndex.putDownloadState(downloadState);
|
||||
downloadIndex.release();
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
downloadIndex = new DefaultDownloadIndex(databaseProvider);
|
||||
DownloadState readDownloadState = downloadIndex.getDownloadState(id);
|
||||
|
||||
assertThat(readDownloadState).isNull();
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeDownloadState_nonExistingId_doesNotFail() {
|
||||
downloadIndex.removeDownloadState("non existing id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeDownloadState_existingId_getDownloadStateReturnsNull() {
|
||||
String id = "id";
|
||||
DownloadState downloadState = new DownloadStateBuilder(id).build();
|
||||
downloadIndex.putDownloadState(downloadState);
|
||||
|
||||
downloadIndex.removeDownloadState(id);
|
||||
DownloadState readDownloadState = downloadIndex.getDownloadState(id);
|
||||
|
||||
assertThat(readDownloadState).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDownloadStates_emptyDownloadIndex_returnsEmptyArray() {
|
||||
assertThat(downloadIndex.getDownloadStates().getCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDownloadStates_noState_returnsAllDownloadStatusSortedByStartTime() {
|
||||
DownloadState downloadState1 = new DownloadStateBuilder("id1").setStartTimeMs(1).build();
|
||||
downloadIndex.putDownloadState(downloadState1);
|
||||
DownloadState downloadState2 = new DownloadStateBuilder("id2").setStartTimeMs(0).build();
|
||||
downloadIndex.putDownloadState(downloadState2);
|
||||
|
||||
DownloadStateCursor cursor = downloadIndex.getDownloadStates();
|
||||
|
||||
assertThat(cursor.getCount()).isEqualTo(2);
|
||||
cursor.moveToNext();
|
||||
assertEqual(cursor.getDownloadState(), downloadState2);
|
||||
cursor.moveToNext();
|
||||
assertEqual(cursor.getDownloadState(), downloadState1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDownloadStates_withStates_returnsAllDownloadStatusWithTheSameStates() {
|
||||
DownloadState downloadState1 =
|
||||
new DownloadStateBuilder("id1")
|
||||
.setStartTimeMs(0)
|
||||
.setState(DownloadState.STATE_REMOVED)
|
||||
.build();
|
||||
downloadIndex.putDownloadState(downloadState1);
|
||||
DownloadState downloadState2 =
|
||||
new DownloadStateBuilder("id2")
|
||||
.setStartTimeMs(1)
|
||||
.setState(DownloadState.STATE_STOPPED)
|
||||
.build();
|
||||
downloadIndex.putDownloadState(downloadState2);
|
||||
DownloadState downloadState3 =
|
||||
new DownloadStateBuilder("id3")
|
||||
.setStartTimeMs(2)
|
||||
.setState(DownloadState.STATE_COMPLETED)
|
||||
.build();
|
||||
downloadIndex.putDownloadState(downloadState3);
|
||||
|
||||
DownloadStateCursor cursor =
|
||||
downloadIndex.getDownloadStates(DownloadState.STATE_REMOVED, DownloadState.STATE_COMPLETED);
|
||||
|
||||
assertThat(cursor.getCount()).isEqualTo(2);
|
||||
cursor.moveToNext();
|
||||
assertEqual(cursor.getDownloadState(), downloadState1);
|
||||
cursor.moveToNext();
|
||||
assertEqual(cursor.getDownloadState(), downloadState3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesTableExist_nonExistingTable_returnsFalse() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
|
||||
assertThat(DefaultDownloadIndex.doesTableExist(databaseProvider, "NonExistingTable")).isFalse();
|
||||
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesTableExist_existingTable_returnsTrue() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
String tableName = "ExistingTable";
|
||||
databaseProvider.getWritableDatabase().execSQL("CREATE TABLE " + tableName + "(dummy)");
|
||||
|
||||
assertThat(DefaultDownloadIndex.doesTableExist(databaseProvider, tableName)).isTrue();
|
||||
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVersion_nonExistingTable_returnsZero() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
DefaultDownloadIndex.VersionTable versionTable =
|
||||
new DefaultDownloadIndex.VersionTable(databaseProvider);
|
||||
|
||||
int version = versionTable.getVersion(FEATURE_OFFLINE);
|
||||
|
||||
assertThat(version).isEqualTo(0);
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVersion_returnsSetVersion() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
DefaultDownloadIndex.VersionTable versionTable =
|
||||
new DefaultDownloadIndex.VersionTable(databaseProvider);
|
||||
|
||||
versionTable.setVersion(FEATURE_OFFLINE, 1);
|
||||
assertThat(versionTable.getVersion(FEATURE_OFFLINE)).isEqualTo(1);
|
||||
|
||||
versionTable.setVersion(FEATURE_OFFLINE, 10);
|
||||
assertThat(versionTable.getVersion(FEATURE_OFFLINE)).isEqualTo(10);
|
||||
|
||||
versionTable.setVersion(FEATURE_CACHE, 5);
|
||||
assertThat(versionTable.getVersion(FEATURE_CACHE)).isEqualTo(5);
|
||||
assertThat(versionTable.getVersion(FEATURE_OFFLINE)).isEqualTo(10);
|
||||
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadStateTableConstructor_noTable_createsTable() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
assertThat(
|
||||
DefaultDownloadIndex.doesTableExist(
|
||||
databaseProvider, DefaultDownloadIndex.DownloadStateTable.TABLE_NAME))
|
||||
.isFalse();
|
||||
|
||||
new DefaultDownloadIndex.DownloadStateTable(databaseProvider);
|
||||
|
||||
assertThat(
|
||||
DefaultDownloadIndex.doesTableExist(
|
||||
databaseProvider, DefaultDownloadIndex.DownloadStateTable.TABLE_NAME))
|
||||
.isTrue();
|
||||
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadStateTableConstructor_versionZero_versionSet() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
|
||||
new DefaultDownloadIndex.DownloadStateTable(databaseProvider);
|
||||
|
||||
DefaultDownloadIndex.VersionTable versionTable =
|
||||
new DefaultDownloadIndex.VersionTable(databaseProvider);
|
||||
assertThat(versionTable.getVersion(FEATURE_OFFLINE))
|
||||
.isEqualTo(DefaultDownloadIndex.DownloadStateTable.TABLE_VERSION);
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadStateTableConstructor_greaterVersion_tableRecreated() {
|
||||
DatabaseProviderImpl databaseProvider = new DatabaseProviderImpl();
|
||||
databaseProvider
|
||||
.getWritableDatabase()
|
||||
.execSQL("CREATE TABLE " + DefaultDownloadIndex.DownloadStateTable.TABLE_NAME + "(dummy)");
|
||||
DefaultDownloadIndex.VersionTable versionTable =
|
||||
new DefaultDownloadIndex.VersionTable(databaseProvider);
|
||||
versionTable.setVersion(FEATURE_OFFLINE, Integer.MAX_VALUE);
|
||||
|
||||
DefaultDownloadIndex.DownloadStateTable downloadStateTable =
|
||||
new DefaultDownloadIndex.DownloadStateTable(databaseProvider);
|
||||
String id = "id";
|
||||
DownloadState downloadState = new DownloadStateBuilder(id).build();
|
||||
downloadStateTable.replace(downloadState);
|
||||
DownloadState readDownloadState = downloadStateTable.get(id);
|
||||
assertEqual(readDownloadState, downloadState);
|
||||
|
||||
assertThat(versionTable.getVersion(FEATURE_OFFLINE))
|
||||
.isEqualTo(DefaultDownloadIndex.DownloadStateTable.TABLE_VERSION);
|
||||
databaseProvider.close();
|
||||
}
|
||||
|
||||
private static void assertEqual(DownloadState downloadState, DownloadState expected) {
|
||||
assertThat(areEqual(downloadState, expected)).isTrue();
|
||||
}
|
||||
|
||||
private static boolean areEqual(DownloadState downloadState, DownloadState that) {
|
||||
if (downloadState.state != that.state) {
|
||||
return false;
|
||||
}
|
||||
if (Float.compare(that.downloadPercentage, downloadState.downloadPercentage) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.downloadedBytes != that.downloadedBytes) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.totalBytes != that.totalBytes) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.startTimeMs != that.startTimeMs) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.updateTimeMs != that.updateTimeMs) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.failureReason != that.failureReason) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.stopFlags != that.stopFlags) {
|
||||
return false;
|
||||
}
|
||||
if (!downloadState.id.equals(that.id)) {
|
||||
return false;
|
||||
}
|
||||
if (!downloadState.type.equals(that.type)) {
|
||||
return false;
|
||||
}
|
||||
if (!downloadState.uri.equals(that.uri)) {
|
||||
return false;
|
||||
}
|
||||
if (downloadState.cacheKey != null
|
||||
? !downloadState.cacheKey.equals(that.cacheKey)
|
||||
: that.cacheKey != null) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(downloadState.streamKeys, that.streamKeys)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(downloadState.customMetadata, that.customMetadata);
|
||||
}
|
||||
|
||||
private static class DownloadStateBuilder {
|
||||
private String id;
|
||||
private String type;
|
||||
private String uri;
|
||||
@Nullable private String cacheKey;
|
||||
private int state;
|
||||
private float downloadPercentage;
|
||||
private long downloadedBytes;
|
||||
private long totalBytes;
|
||||
private int failureReason;
|
||||
private int stopFlags;
|
||||
private long startTimeMs;
|
||||
private long updateTimeMs;
|
||||
private StreamKey[] streamKeys;
|
||||
private byte[] customMetadata;
|
||||
|
||||
private DownloadStateBuilder(String id) {
|
||||
this.id = id;
|
||||
this.type = "type";
|
||||
this.uri = "uri";
|
||||
this.cacheKey = null;
|
||||
this.state = DownloadState.STATE_QUEUED;
|
||||
this.downloadPercentage = (float) C.PERCENTAGE_UNSET;
|
||||
this.downloadedBytes = (long) 0;
|
||||
this.totalBytes = (long) C.LENGTH_UNSET;
|
||||
this.failureReason = DownloadState.FAILURE_REASON_NONE;
|
||||
this.stopFlags = 0;
|
||||
this.startTimeMs = (long) 0;
|
||||
this.updateTimeMs = (long) 0;
|
||||
this.streamKeys = new StreamKey[0];
|
||||
this.customMetadata = new byte[0];
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setUri(String uri) {
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setCacheKey(@Nullable String cacheKey) {
|
||||
this.cacheKey = cacheKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setState(int state) {
|
||||
this.state = state;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setDownloadPercentage(float downloadPercentage) {
|
||||
this.downloadPercentage = downloadPercentage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setDownloadedBytes(long downloadedBytes) {
|
||||
this.downloadedBytes = downloadedBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setTotalBytes(long totalBytes) {
|
||||
this.totalBytes = totalBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setFailureReason(int failureReason) {
|
||||
this.failureReason = failureReason;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setStopFlags(int stopFlags) {
|
||||
this.stopFlags = stopFlags;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setStartTimeMs(long startTimeMs) {
|
||||
this.startTimeMs = startTimeMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setUpdateTimeMs(long updateTimeMs) {
|
||||
this.updateTimeMs = updateTimeMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setStreamKeys(StreamKey... streamKeys) {
|
||||
this.streamKeys = streamKeys;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadStateBuilder setCustomMetadata(byte[] customMetadata) {
|
||||
this.customMetadata = customMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownloadState build() {
|
||||
return new DownloadState(
|
||||
id,
|
||||
type,
|
||||
Uri.parse(uri),
|
||||
cacheKey,
|
||||
state,
|
||||
downloadPercentage,
|
||||
downloadedBytes,
|
||||
totalBytes,
|
||||
failureReason,
|
||||
stopFlags,
|
||||
startTimeMs,
|
||||
updateTimeMs,
|
||||
streamKeys,
|
||||
customMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DatabaseProviderImpl extends SQLiteOpenHelper
|
||||
implements DefaultDownloadIndex.DatabaseProvider {
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final String DATABASE_NAME = "TestExoPlayerDownloadIndex.db";
|
||||
|
||||
public DatabaseProviderImpl() {
|
||||
super(RuntimeEnvironment.application, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user