From 18dcad9b8d825ed1afe56985b3ea065376c20989 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 9 Apr 2019 00:26:07 +0100 Subject: [PATCH] Simplify DownloadState to contain its DownloadAction This makes sense now that DownloadAction is only for downloading, and not also for removal. PiperOrigin-RevId: 242561575 --- .../exoplayer2/demo/DemoDownloadService.java | 4 +- .../exoplayer2/demo/DownloadTracker.java | 23 ++-- .../offline/DefaultDownloadIndex.java | 120 +++++++++--------- .../exoplayer2/offline/DownloadAction.java | 28 ++++ .../exoplayer2/offline/DownloadIndexUtil.java | 2 +- .../exoplayer2/offline/DownloadManager.java | 38 ++---- .../exoplayer2/offline/DownloadState.java | 84 +++--------- .../offline/DownloadIndexUtilTest.java | 17 +-- .../offline/DownloadManagerTest.java | 2 +- .../offline/DownloadStateBuilder.java | 28 ++-- .../exoplayer2/offline/DownloadStateTest.java | 29 ++--- .../testutil/TestDownloadManagerListener.java | 8 +- 12 files changed, 161 insertions(+), 222 deletions(-) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java index 91e2aa5bcc..ae60337c76 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java @@ -74,13 +74,13 @@ public class DemoDownloadService extends DownloadService { notificationHelper.buildDownloadCompletedNotification( R.drawable.ic_download_done, /* contentIntent= */ null, - Util.fromUtf8Bytes(downloadState.customMetadata)); + Util.fromUtf8Bytes(downloadState.action.data)); } else if (downloadState.state == DownloadState.STATE_FAILED) { notification = notificationHelper.buildDownloadFailedNotification( R.drawable.ic_download_done, /* contentIntent= */ null, - Util.fromUtf8Bytes(downloadState.customMetadata)); + Util.fromUtf8Bytes(downloadState.action.data)); } else { return; } diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java index 077f0bffb1..a51c60f8b6 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java @@ -37,7 +37,6 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -96,7 +95,7 @@ public class DownloadTracker implements DownloadManager.Listener { public List getOfflineStreamKeys(Uri uri) { DownloadState downloadState = downloadStates.get(uri); return downloadState != null && downloadState.state != DownloadState.STATE_FAILED - ? Arrays.asList(downloadState.streamKeys) + ? downloadState.action.streamKeys : Collections.emptyList(); } @@ -109,7 +108,7 @@ public class DownloadTracker implements DownloadManager.Listener { DownloadState downloadState = downloadStates.get(uri); if (downloadState != null) { DownloadService.startWithRemoveDownload( - context, DemoDownloadService.class, downloadState.id, /* foreground= */ false); + context, DemoDownloadService.class, downloadState.action.id, /* foreground= */ false); } else { if (startDownloadDialogHelper != null) { startDownloadDialogHelper.release(); @@ -124,21 +123,17 @@ public class DownloadTracker implements DownloadManager.Listener { @Override public void onDownloadStateChanged(DownloadManager downloadManager, DownloadState downloadState) { - boolean downloadAdded = downloadStates.put(downloadState.uri, downloadState) == null; - if (downloadAdded) { - for (Listener listener : listeners) { - listener.onDownloadsChanged(); - } + downloadStates.put(downloadState.action.uri, downloadState); + for (Listener listener : listeners) { + listener.onDownloadsChanged(); } } @Override public void onDownloadRemoved(DownloadManager downloadManager, DownloadState downloadState) { - boolean downloadRemoved = downloadStates.remove(downloadState.uri) != null; - if (downloadRemoved) { - for (Listener listener : listeners) { - listener.onDownloadsChanged(); - } + downloadStates.remove(downloadState.action.uri); + for (Listener listener : listeners) { + listener.onDownloadsChanged(); } } @@ -148,7 +143,7 @@ public class DownloadTracker implements DownloadManager.Listener { try (DownloadStateCursor loadedDownloadStates = downloadIndex.getDownloadStates()) { while (loadedDownloadStates.moveToNext()) { DownloadState downloadState = loadedDownloadStates.getDownloadState(); - downloadStates.put(downloadState.uri, downloadState); + downloadStates.put(downloadState.action.uri, downloadState); } } catch (IOException e) { Log.w(TAG, "Failed to query download states", e); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java index 273c9dc9b9..32e7c3a66f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java @@ -29,6 +29,8 @@ import com.google.android.exoplayer2.database.VersionTable; import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; +import java.util.ArrayList; +import java.util.List; /** * A {@link DownloadIndex} which uses SQLite to persist {@link DownloadState}s. @@ -38,8 +40,7 @@ import com.google.android.exoplayer2.util.Util; */ public final class DefaultDownloadIndex implements DownloadIndex { - @VisibleForTesting - /* package */ static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Downloads"; + private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Downloads"; // TODO: Support multiple instances. Probably using the underlying cache UID. @VisibleForTesting /* package */ static final String INSTANCE_UID = "singleton"; @@ -48,7 +49,9 @@ public final class DefaultDownloadIndex implements DownloadIndex { 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_STREAM_KEYS = "stream_keys"; + private static final String COLUMN_CUSTOM_CACHE_KEY = "cache_key"; + private static final String COLUMN_DATA = "custom_metadata"; 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"; @@ -58,8 +61,6 @@ public final class DefaultDownloadIndex implements DownloadIndex { private static final String COLUMN_MANUAL_STOP_REASON = "manual_stop_reason"; 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"; @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated @@ -68,18 +69,18 @@ public final class DefaultDownloadIndex implements DownloadIndex { 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_NOT_MET_REQUIREMENTS = 9; - private static final int COLUMN_INDEX_MANUAL_STOP_REASON = 10; - private static final int COLUMN_INDEX_START_TIME_MS = 11; - private static final int COLUMN_INDEX_UPDATE_TIME_MS = 12; - private static final int COLUMN_INDEX_STREAM_KEYS = 13; - private static final int COLUMN_INDEX_CUSTOM_METADATA = 14; + 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_DATA = 5; + private static final int COLUMN_INDEX_STATE = 6; + private static final int COLUMN_INDEX_DOWNLOAD_PERCENTAGE = 7; + private static final int COLUMN_INDEX_DOWNLOADED_BYTES = 8; + private static final int COLUMN_INDEX_TOTAL_BYTES = 9; + private static final int COLUMN_INDEX_FAILURE_REASON = 10; + private static final int COLUMN_INDEX_NOT_MET_REQUIREMENTS = 11; + private static final int COLUMN_INDEX_MANUAL_STOP_REASON = 12; + private static final int COLUMN_INDEX_START_TIME_MS = 13; + private static final int COLUMN_INDEX_UPDATE_TIME_MS = 14; private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?"; private static final String WHERE_STATE_TERMINAL = @@ -90,7 +91,9 @@ public final class DefaultDownloadIndex implements DownloadIndex { COLUMN_ID, COLUMN_TYPE, COLUMN_URI, - COLUMN_CACHE_KEY, + COLUMN_STREAM_KEYS, + COLUMN_CUSTOM_CACHE_KEY, + COLUMN_DATA, COLUMN_STATE, COLUMN_DOWNLOAD_PERCENTAGE, COLUMN_DOWNLOADED_BYTES, @@ -99,9 +102,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { COLUMN_NOT_MET_REQUIREMENTS, COLUMN_MANUAL_STOP_REASON, COLUMN_START_TIME_MS, - COLUMN_UPDATE_TIME_MS, - COLUMN_STREAM_KEYS, - COLUMN_CUSTOM_METADATA + COLUMN_UPDATE_TIME_MS }; private static final String SQL_DROP_TABLE_IF_EXISTS = "DROP TABLE IF EXISTS " + TABLE_NAME; @@ -115,7 +116,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { + " TEXT NOT NULL," + COLUMN_URI + " TEXT NOT NULL," - + COLUMN_CACHE_KEY + + COLUMN_CUSTOM_CACHE_KEY + " TEXT," + COLUMN_STATE + " INTEGER NOT NULL," @@ -139,7 +140,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { + " INTEGER NOT NULL," + COLUMN_STREAM_KEYS + " TEXT NOT NULL," - + COLUMN_CUSTOM_METADATA + + COLUMN_DATA + " BLOB NOT NULL)"; private static final String TRUE = "1"; @@ -168,9 +169,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { return null; } cursor.moveToNext(); - DownloadState downloadState = getDownloadStateForCurrentRow(cursor); - Assertions.checkState(id.equals(downloadState.id)); - return downloadState; + return getDownloadStateForCurrentRow(cursor); } catch (SQLiteException e) { throw new DatabaseIOException(e); } @@ -192,24 +191,24 @@ public final class DefaultDownloadIndex implements DownloadIndex { */ public void putDownloadState(DownloadState downloadState) throws DatabaseIOException { ensureInitialized(); + ContentValues values = new ContentValues(); + values.put(COLUMN_ID, downloadState.action.id); + values.put(COLUMN_TYPE, downloadState.action.type); + values.put(COLUMN_URI, downloadState.action.uri.toString()); + values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(downloadState.action.streamKeys)); + values.put(COLUMN_CUSTOM_CACHE_KEY, downloadState.action.customCacheKey); + values.put(COLUMN_DATA, downloadState.action.data); + values.put(COLUMN_STATE, downloadState.state); + values.put(COLUMN_DOWNLOAD_PERCENTAGE, downloadState.getDownloadPercentage()); + values.put(COLUMN_DOWNLOADED_BYTES, downloadState.getDownloadedBytes()); + values.put(COLUMN_TOTAL_BYTES, downloadState.getTotalBytes()); + values.put(COLUMN_FAILURE_REASON, downloadState.failureReason); + values.put(COLUMN_STOP_FLAGS, 0); + values.put(COLUMN_NOT_MET_REQUIREMENTS, downloadState.notMetRequirements); + values.put(COLUMN_MANUAL_STOP_REASON, downloadState.manualStopReason); + values.put(COLUMN_START_TIME_MS, downloadState.startTimeMs); + values.put(COLUMN_UPDATE_TIME_MS, downloadState.updateTimeMs); try { - 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.getDownloadPercentage()); - values.put(COLUMN_DOWNLOADED_BYTES, downloadState.getDownloadedBytes()); - values.put(COLUMN_TOTAL_BYTES, downloadState.getTotalBytes()); - values.put(COLUMN_FAILURE_REASON, downloadState.failureReason); - values.put(COLUMN_STOP_FLAGS, /*stopFlags*/ 0); - values.put(COLUMN_NOT_MET_REQUIREMENTS, downloadState.notMetRequirements); - values.put(COLUMN_MANUAL_STOP_REASON, downloadState.manualStopReason); - 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.replaceOrThrow(TABLE_NAME, /* nullColumnHack= */ null, values); } catch (SQLiteException e) { @@ -341,29 +340,33 @@ public final class DefaultDownloadIndex implements DownloadIndex { } private static DownloadState getDownloadStateForCurrentRow(Cursor cursor) { + DownloadAction action = + DownloadAction.createDownloadAction( + cursor.getString(COLUMN_INDEX_ID), + cursor.getString(COLUMN_INDEX_TYPE), + Uri.parse(cursor.getString(COLUMN_INDEX_URI)), + decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)), + cursor.getString(COLUMN_INDEX_CUSTOM_CACHE_KEY), + cursor.getBlob(COLUMN_INDEX_DATA)); CachingCounters cachingCounters = new CachingCounters(); cachingCounters.alreadyCachedBytes = cursor.getLong(COLUMN_INDEX_DOWNLOADED_BYTES); cachingCounters.contentLength = cursor.getLong(COLUMN_INDEX_TOTAL_BYTES); cachingCounters.percentage = cursor.getFloat(COLUMN_INDEX_DOWNLOAD_PERCENTAGE); 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), + action, cursor.getInt(COLUMN_INDEX_STATE), cursor.getInt(COLUMN_INDEX_FAILURE_REASON), cursor.getInt(COLUMN_INDEX_NOT_MET_REQUIREMENTS), cursor.getInt(COLUMN_INDEX_MANUAL_STOP_REASON), 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), cachingCounters); } - private static String encodeStreamKeys(StreamKey[] streamKeys) { + private static String encodeStreamKeys(List streamKeys) { StringBuilder stringBuilder = new StringBuilder(); - for (StreamKey streamKey : streamKeys) { + for (int i = 0; i < streamKeys.size(); i++) { + StreamKey streamKey = streamKeys.get(i); stringBuilder .append(streamKey.periodIndex) .append('.') @@ -378,21 +381,20 @@ public final class DefaultDownloadIndex implements DownloadIndex { return stringBuilder.toString(); } - private static StreamKey[] decodeStreamKeys(String encodedStreamKeys) { + private static List decodeStreamKeys(String encodedStreamKeys) { + ArrayList streamKeys = new ArrayList<>(); if (encodedStreamKeys.isEmpty()) { - return new StreamKey[0]; + return streamKeys; } 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], "\\."); + for (String streamKeysString : streamKeysStrings) { + String[] indices = Util.split(streamKeysString, "\\."); Assertions.checkState(indices.length == 3); - streamKeys[i] = + streamKeys.add( new StreamKey( Integer.parseInt(indices[0]), Integer.parseInt(indices[1]), - Integer.parseInt(indices[2])); + Integer.parseInt(indices[2]))); } return streamKeys; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java index 32f36d051d..c0e1ab66ab 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadAction.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.offline; import android.net.Uri; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -133,6 +134,33 @@ public final class DownloadAction { this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY; } + /** + * Returns the result of merging {@code newAction} into this action. + * + * @param newAction The new action. + * @return The merged result. + */ + public DownloadAction copyWithMergedAction(DownloadAction newAction) { + Assertions.checkState(id.equals(newAction.id)); + Assertions.checkState(type.equals(newAction.type)); + + List mergedKeys; + if (streamKeys.isEmpty() || newAction.streamKeys.isEmpty()) { + // If either streamKeys is empty then all streams should be downloaded. + mergedKeys = Collections.emptyList(); + } else { + mergedKeys = new ArrayList<>(streamKeys); + for (int i = 0; i < newAction.streamKeys.size(); i++) { + StreamKey newKey = newAction.streamKeys.get(i); + if (!mergedKeys.contains(newKey)) { + mergedKeys.add(newKey); + } + } + } + return new DownloadAction( + id, type, newAction.uri, mergedKeys, newAction.customCacheKey, newAction.data); + } + /** Serializes itself into a byte array. */ public byte[] toByteArray() { ByteArrayOutputStream output = new ByteArrayOutputStream(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadIndexUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadIndexUtil.java index 524e71e9e2..4cbf35f523 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadIndexUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadIndexUtil.java @@ -76,7 +76,7 @@ public final class DownloadIndexUtil { throws IOException { DownloadState downloadState = downloadIndex.getDownloadState(id != null ? id : action.id); if (downloadState != null) { - downloadState = downloadState.mergeAction(action); + downloadState = downloadState.copyWithMergedAction(action); } else { downloadState = new DownloadState(action); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java index 9c9c75d71f..57cea6a54e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java @@ -47,7 +47,6 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; @@ -504,7 +503,7 @@ public final class DownloadManager { } private void onDownloadStateChanged(DownloadState downloadState) { - int downloadStateIndex = getDownloadStateIndex(downloadState.id); + int downloadStateIndex = getDownloadStateIndex(downloadState.action.id); if (downloadState.state == STATE_COMPLETED || downloadState.state == STATE_FAILED) { if (downloadStateIndex != C.INDEX_UNSET) { downloadStates.remove(downloadStateIndex); @@ -520,7 +519,7 @@ public final class DownloadManager { } private void onDownloadRemoved(DownloadState downloadState) { - downloadStates.remove(getDownloadStateIndex(downloadState.id)); + downloadStates.remove(getDownloadStateIndex(downloadState.action.id)); for (Listener listener : listeners) { listener.onDownloadRemoved(this, downloadState); } @@ -538,7 +537,7 @@ public final class DownloadManager { private int getDownloadStateIndex(String id) { for (int i = 0; i < downloadStates.size(); i++) { - if (downloadStates.get(i).id.equals(id)) { + if (downloadStates.get(i).action.id.equals(id)) { return i; } } @@ -624,7 +623,7 @@ public final class DownloadManager { downloadState = new DownloadState(action); logd("Download state is created for " + action.id); } else { - downloadState = downloadState.mergeAction(action); + downloadState = downloadState.copyWithMergedAction(action); logd("Download state is loaded for " + action.id); } addDownloadForState(downloadState); @@ -638,7 +637,7 @@ public final class DownloadManager { } else { DownloadState downloadState = loadDownloadState(id); if (downloadState != null) { - addDownloadForState(downloadState.setRemoveState()); + addDownloadForState(downloadState.copyWithState(STATE_REMOVING)); } else { logd("Can't remove download. No download with id: " + id); } @@ -661,7 +660,7 @@ public final class DownloadManager { private void onDownloadRemovedInternal(Download download, DownloadState downloadState) { logd("Download is removed", download); try { - downloadIndex.removeDownloadState(downloadState.id); + downloadIndex.removeDownloadState(downloadState.action.id); } catch (DatabaseIOException e) { Log.e(TAG, "Failed to remove from index", e); } @@ -837,11 +836,11 @@ public final class DownloadManager { } public String getId() { - return downloadState.id; + return downloadState.action.id; } public void addAction(DownloadAction newAction) { - downloadState = downloadState.mergeAction(newAction); + downloadState = downloadState.copyWithMergedAction(newAction); initialize(); } @@ -852,18 +851,13 @@ public final class DownloadManager { public DownloadState getUpdatedDownloadState() { downloadState = new DownloadState( - downloadState.id, - downloadState.type, - downloadState.uri, - downloadState.cacheKey, + downloadState.action, state, state != STATE_FAILED ? FAILURE_REASON_NONE : failureReason, notMetRequirements, manualStopReason, downloadState.startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), - downloadState.streamKeys, - downloadState.customMetadata, downloadState.counters); return downloadState; } @@ -895,16 +889,6 @@ public final class DownloadManager { updateStopState(); } - public DownloadAction getAction() { - return DownloadAction.createDownloadAction( - downloadState.id, - downloadState.type, - downloadState.uri, - Arrays.asList(downloadState.streamKeys), - downloadState.cacheKey, - downloadState.customMetadata); - } - public boolean isInRemoveState() { return state == STATE_REMOVING || state == STATE_RESTARTING; } @@ -980,7 +964,7 @@ public final class DownloadManager { initialize(STATE_QUEUED); } else { // STATE_DOWNLOADING if (error != null) { - Log.e(TAG, "Download failed: " + downloadState.id, error); + Log.e(TAG, "Download failed: " + downloadState.action.id, error); failureReason = FAILURE_REASON_UNKNOWN; setState(STATE_FAILED); } else { @@ -1001,7 +985,7 @@ public final class DownloadManager { private DownloadThread(Download download) { this.download = download; - this.downloader = downloaderFactory.createDownloader(download.getAction()); + this.downloader = downloaderFactory.createDownloader(download.downloadState.action); this.isRemoveThread = download.isInRemoveState(); start(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java index c98f1dcf39..5e7a54b7e7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadState.java @@ -15,9 +15,7 @@ */ package com.google.android.exoplayer2.offline; -import android.net.Uri; import androidx.annotation.IntDef; -import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.scheduler.Requirements.RequirementFlags; @@ -26,8 +24,6 @@ import com.google.android.exoplayer2.util.Assertions; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collections; -import java.util.HashSet; /** Represents state of a download. */ public final class DownloadState { @@ -114,24 +110,15 @@ public final class DownloadState { } } - /** The unique content id. */ - public final String id; - /** The type of the content. */ - public final String type; - /** The Uri of the content. */ - public final Uri uri; - /** A custom key for cache indexing. */ - @Nullable public final String cacheKey; + /** The download action. */ + public final DownloadAction action; + /** The state of the download. */ @State public final int state; /** The first time when download entry is created. */ public final long startTimeMs; /** The last update time. */ public final long updateTimeMs; - /** Keys of streams to be downloaded. If empty, all streams will be downloaded. */ - public final StreamKey[] streamKeys; - /** Optional custom data. */ - public final byte[] customMetadata; /** * If {@link #state} is {@link #STATE_FAILED} then this is the cause, otherwise {@link * #FAILURE_REASON_NONE}. @@ -155,52 +142,37 @@ public final class DownloadState { private DownloadState(DownloadAction action, long currentTimeMs) { this( - action.id, - action.type, - action.uri, - action.customCacheKey, + action, /* state= */ STATE_QUEUED, FAILURE_REASON_NONE, /* notMetRequirements= */ 0, /* manualStopReason= */ 0, /* startTimeMs= */ currentTimeMs, /* updateTimeMs= */ currentTimeMs, - action.streamKeys.toArray(new StreamKey[0]), - action.data, new CachingCounters()); } /* package */ DownloadState( - String id, - String type, - Uri uri, - @Nullable String cacheKey, + DownloadAction action, @State int state, @FailureReason int failureReason, @RequirementFlags int notMetRequirements, int manualStopReason, long startTimeMs, long updateTimeMs, - StreamKey[] streamKeys, - byte[] customMetadata, CachingCounters counters) { Assertions.checkNotNull(counters); Assertions.checkState((failureReason == FAILURE_REASON_NONE) == (state != STATE_FAILED)); if (manualStopReason != 0 || notMetRequirements != 0) { Assertions.checkState(state != STATE_DOWNLOADING && state != STATE_QUEUED); } - this.id = id; - this.type = type; - this.uri = uri; - this.cacheKey = cacheKey; + this.action = action; this.state = state; this.failureReason = failureReason; this.notMetRequirements = notMetRequirements; this.manualStopReason = manualStopReason; this.startTimeMs = startTimeMs; this.updateTimeMs = updateTimeMs; - this.streamKeys = streamKeys; - this.customMetadata = customMetadata; this.counters = counters; } @@ -208,43 +180,35 @@ public final class DownloadState { * Merges the given {@link DownloadAction} and creates a new {@link DownloadState}. The action * must have the same id and type. * - * @param action The {@link DownloadAction} to be merged. + * @param newAction The {@link DownloadAction} to be merged. * @return A new {@link DownloadState}. */ - public DownloadState mergeAction(DownloadAction action) { - Assertions.checkArgument(action.id.equals(id)); - Assertions.checkArgument(action.type.equals(type)); + public DownloadState copyWithMergedAction(DownloadAction newAction) { return new DownloadState( - id, - type, - action.uri, - action.customCacheKey, + action.copyWithMergedAction(newAction), getNextState(state, manualStopReason != 0 || notMetRequirements != 0), FAILURE_REASON_NONE, notMetRequirements, manualStopReason, startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), - mergeStreamKeys(this, action), - action.data, counters); } - /** Returns a duplicate {@link DownloadState} in {@link #STATE_REMOVING}. */ - public DownloadState setRemoveState() { + /** + * Returns a copy with the specified state, clearing {@link #failureReason}. + * + * @param state The {@link State}. + */ + public DownloadState copyWithState(@State int state) { return new DownloadState( - id, - type, - uri, - cacheKey, - STATE_REMOVING, + action, + state, FAILURE_REASON_NONE, notMetRequirements, manualStopReason, startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), - streamKeys, - customMetadata, counters); } @@ -285,18 +249,4 @@ public final class DownloadState { return STATE_QUEUED; } } - - private static StreamKey[] mergeStreamKeys(DownloadState downloadState, DownloadAction action) { - StreamKey[] streamKeys = downloadState.streamKeys; - if (streamKeys.length > 0) { - if (action.streamKeys.isEmpty()) { - streamKeys = new StreamKey[0]; - } else { - HashSet keys = new HashSet<>(action.streamKeys); - Collections.addAll(keys, downloadState.streamKeys); - streamKeys = keys.toArray(new StreamKey[0]); - } - } - return streamKeys; - } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java index 17f3d38ba1..d321ba00d1 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadIndexUtilTest.java @@ -102,11 +102,11 @@ public class DownloadIndexUtilTest { DownloadState downloadState = downloadIndex.getDownloadState(action2.id); assertThat(downloadState).isNotNull(); - assertThat(downloadState.type).isEqualTo(action2.type); - assertThat(downloadState.cacheKey).isEqualTo(action2.customCacheKey); - assertThat(downloadState.customMetadata).isEqualTo(action2.data); - assertThat(downloadState.uri).isEqualTo(action2.uri); - assertThat(Arrays.asList(downloadState.streamKeys)).containsExactly(streamKey1, streamKey2); + assertThat(downloadState.action.type).isEqualTo(action2.type); + assertThat(downloadState.action.customCacheKey).isEqualTo(action2.customCacheKey); + assertThat(downloadState.action.data).isEqualTo(action2.data); + assertThat(downloadState.action.uri).isEqualTo(action2.uri); + assertThat(downloadState.action.streamKeys).containsExactly(streamKey1, streamKey2); assertThat(downloadState.state).isEqualTo(DownloadState.STATE_QUEUED); } @@ -151,12 +151,7 @@ public class DownloadIndexUtilTest { private void assertDownloadIndexContainsAction(DownloadAction action, int state) throws IOException { DownloadState downloadState = downloadIndex.getDownloadState(action.id); - assertThat(downloadState.type).isEqualTo(action.type); - assertThat(downloadState.cacheKey).isEqualTo(action.customCacheKey); - assertThat(downloadState.customMetadata).isEqualTo(action.data); - assertThat(downloadState.uri).isEqualTo(action.uri); - assertThat(Arrays.asList(downloadState.streamKeys)) - .containsExactlyElementsIn(action.streamKeys); + assertThat(downloadState.action).isEqualTo(action); assertThat(downloadState.state).isEqualTo(state); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index 2c1dd233d3..e25b2913e3 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -347,7 +347,7 @@ public class DownloadManagerTest { assertThat(states).hasLength(3); String[] taskIds = {task1.taskId, task2.taskId, task3.taskId}; - String[] stateTaskIds = {states[0].id, states[1].id, states[2].id}; + String[] stateTaskIds = {states[0].action.id, states[1].action.id, states[2].action.id}; assertThat(stateTaskIds).isEqualTo(taskIds); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateBuilder.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateBuilder.java index d536fd8176..6a41494a6f 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateBuilder.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateBuilder.java @@ -18,6 +18,9 @@ package com.google.android.exoplayer2.offline; import android.net.Uri; import androidx.annotation.Nullable; import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Builder for DownloadState. @@ -38,21 +41,15 @@ class DownloadStateBuilder { private int manualStopReason; private long startTimeMs; private long updateTimeMs; - private StreamKey[] streamKeys; + private List streamKeys; private byte[] customMetadata; DownloadStateBuilder(String id) { - this(id, "type", Uri.parse("uri"), /* cacheKey= */ null, new byte[0], new StreamKey[0]); + this(id, "type", Uri.parse("uri"), /* cacheKey= */ null, new byte[0], Collections.emptyList()); } DownloadStateBuilder(DownloadAction action) { - this( - action.id, - action.type, - action.uri, - action.customCacheKey, - action.data, - action.streamKeys.toArray(new StreamKey[0])); + this(action.id, action.type, action.uri, action.customCacheKey, action.data, action.streamKeys); } DownloadStateBuilder( @@ -61,7 +58,7 @@ class DownloadStateBuilder { Uri uri, String cacheKey, byte[] customMetadata, - StreamKey[] streamKeys) { + List streamKeys) { this.id = id; this.type = type; this.uri = uri; @@ -146,7 +143,7 @@ class DownloadStateBuilder { } public DownloadStateBuilder setStreamKeys(StreamKey... streamKeys) { - this.streamKeys = streamKeys; + this.streamKeys = Arrays.asList(streamKeys); return this; } @@ -156,19 +153,16 @@ class DownloadStateBuilder { } public DownloadState build() { + DownloadAction action = + DownloadAction.createDownloadAction(id, type, uri, streamKeys, cacheKey, customMetadata); return new DownloadState( - id, - type, - uri, - cacheKey, + action, state, failureReason, notMetRequirements, manualStopReason, startTimeMs, updateTimeMs, - streamKeys, - customMetadata, counters); } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java index a073f44cbe..4028677bb2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadStateTest.java @@ -47,7 +47,7 @@ public class DownloadStateTest { .build(); try { - downloadState.mergeAction(downloadAction); + downloadState.copyWithMergedAction(downloadAction); fail(); } catch (Exception e) { // Expected. @@ -64,7 +64,7 @@ public class DownloadStateTest { .build(); try { - downloadState.mergeAction(downloadAction); + downloadState.copyWithMergedAction(downloadAction); fail(); } catch (Exception e) { // Expected. @@ -78,7 +78,7 @@ public class DownloadStateTest { new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_QUEUED); DownloadState downloadState = downloadStateBuilder.build(); - downloadState.mergeAction(downloadAction); + downloadState.copyWithMergedAction(downloadAction); } @Test @@ -90,7 +90,7 @@ public class DownloadStateTest { .setState(DownloadState.STATE_QUEUED); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setUri(downloadAction.uri).build(); assertEqual(mergedDownloadState, expectedDownloadState); @@ -112,7 +112,7 @@ public class DownloadStateTest { .setCustomMetadata(new byte[0]); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setCustomMetadata(downloadAction.data).build(); @@ -126,7 +126,7 @@ public class DownloadStateTest { new DownloadStateBuilder(downloadAction).setState(DownloadState.STATE_REMOVING); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setState(DownloadState.STATE_RESTARTING).build(); @@ -142,7 +142,7 @@ public class DownloadStateTest { .setFailureReason(DownloadState.FAILURE_REASON_UNKNOWN); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder @@ -161,7 +161,7 @@ public class DownloadStateTest { .setManualStopReason(DownloadState.MANUAL_STOP_REASON_UNDEFINED); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); assertEqual(mergedDownloadState, downloadState); } @@ -175,7 +175,7 @@ public class DownloadStateTest { .setManualStopReason(DownloadState.MANUAL_STOP_REASON_UNDEFINED); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setState(DownloadState.STATE_STOPPED).build(); @@ -191,7 +191,7 @@ public class DownloadStateTest { .setNotMetRequirements(0x12345678); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setState(DownloadState.STATE_STOPPED).build(); @@ -259,7 +259,7 @@ public class DownloadStateTest { .setStreamKeys(keys1); DownloadState downloadState = downloadStateBuilder.build(); - DownloadState mergedDownloadState = downloadState.mergeAction(downloadAction); + DownloadState mergedDownloadState = downloadState.copyWithMergedAction(downloadAction); DownloadState expectedDownloadState = downloadStateBuilder.setStreamKeys(expectedKeys).build(); assertEqual(mergedDownloadState, expectedDownloadState); @@ -271,6 +271,7 @@ public class DownloadStateTest { static void assertEqual( DownloadState downloadState, DownloadState that, boolean compareTimeFields) { + assertThat(downloadState.action).isEqualTo(that.action); assertThat(downloadState.state).isEqualTo(that.state); assertThat(downloadState.getDownloadPercentage()).isEqualTo(that.getDownloadPercentage()); assertThat(downloadState.getDownloadedBytes()).isEqualTo(that.getDownloadedBytes()); @@ -282,12 +283,6 @@ public class DownloadStateTest { assertThat(downloadState.failureReason).isEqualTo(that.failureReason); assertThat(downloadState.manualStopReason).isEqualTo(that.manualStopReason); assertThat(downloadState.notMetRequirements).isEqualTo(that.notMetRequirements); - assertThat(downloadState.id).isEqualTo(that.id); - assertThat(downloadState.type).isEqualTo(that.type); - assertThat(downloadState.uri).isEqualTo(that.uri); - assertThat(downloadState.cacheKey).isEqualTo(that.cacheKey); - assertThat(Arrays.asList(downloadState.streamKeys)).containsExactlyElementsIn(that.streamKeys); - assertThat(downloadState.customMetadata).isEqualTo(that.customMetadata); } private DownloadAction createDownloadAction() { diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java index c594da06de..907e104643 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java @@ -57,10 +57,6 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen return getStateQueue(taskId).poll(timeoutMs, TimeUnit.MILLISECONDS); } - public void clearDownloadError() { - this.failureReason = DownloadState.FAILURE_REASON_NONE; - } - @Override public void onInitialized(DownloadManager downloadManager) { initializedCondition.open(); @@ -77,12 +73,12 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen if (downloadState.state == DownloadState.STATE_FAILED) { failureReason = downloadState.failureReason; } - getStateQueue(downloadState.id).add(downloadState.state); + getStateQueue(downloadState.action.id).add(downloadState.state); } @Override public void onDownloadRemoved(DownloadManager downloadManager, DownloadState downloadState) { - getStateQueue(downloadState.id).add(STATE_REMOVED); + getStateQueue(downloadState.action.id).add(STATE_REMOVED); } @Override