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 28a5abafb9..71ee76d0f7 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 @@ -132,6 +132,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { 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_NOT_MET_REQUIREMENTS = "not_met_requirements"; 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"; @@ -147,10 +148,11 @@ public final class DefaultDownloadIndex implements DownloadIndex { 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 int COLUMN_INDEX_NOT_MET_REQUIREMENTS = 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 String COLUMN_SELECTION_ID = COLUMN_ID + " = ?"; @@ -166,6 +168,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { COLUMN_TOTAL_BYTES, COLUMN_FAILURE_REASON, COLUMN_STOP_FLAGS, + COLUMN_NOT_MET_REQUIREMENTS, COLUMN_START_TIME_MS, COLUMN_UPDATE_TIME_MS, COLUMN_STREAM_KEYS, @@ -197,6 +200,8 @@ public final class DefaultDownloadIndex implements DownloadIndex { + " INTEGER NOT NULL," + COLUMN_STOP_FLAGS + " INTEGER NOT NULL," + + COLUMN_NOT_MET_REQUIREMENTS + + " INTEGER NOT NULL," + COLUMN_START_TIME_MS + " INTEGER NOT NULL," + COLUMN_UPDATE_TIME_MS @@ -241,6 +246,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { values.put(COLUMN_TOTAL_BYTES, downloadState.totalBytes); values.put(COLUMN_FAILURE_REASON, downloadState.failureReason); values.put(COLUMN_STOP_FLAGS, downloadState.stopFlags); + values.put(COLUMN_NOT_MET_REQUIREMENTS, downloadState.notMetRequirements); values.put(COLUMN_START_TIME_MS, downloadState.startTimeMs); values.put(COLUMN_UPDATE_TIME_MS, downloadState.updateTimeMs); values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(downloadState.streamKeys)); @@ -312,6 +318,7 @@ public final class DefaultDownloadIndex implements DownloadIndex { cursor.getLong(COLUMN_INDEX_TOTAL_BYTES), cursor.getInt(COLUMN_INDEX_FAILURE_REASON), cursor.getInt(COLUMN_INDEX_STOP_FLAGS), + cursor.getInt(COLUMN_INDEX_NOT_MET_REQUIREMENTS), cursor.getLong(COLUMN_INDEX_START_TIME_MS), cursor.getLong(COLUMN_INDEX_UPDATE_TIME_MS), decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)), 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 63602c7641..e0c914408b 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 @@ -115,6 +115,7 @@ public final class DownloadIndexUtil { /* totalBytes= */ C.LENGTH_UNSET, downloadState.failureReason, downloadState.stopFlags, + downloadState.notMetRequirements, downloadState.startTimeMs, downloadState.updateTimeMs, newKeys, @@ -136,6 +137,7 @@ public final class DownloadIndexUtil { /* totalBytes= */ C.LENGTH_UNSET, DownloadState.FAILURE_REASON_NONE, /* stopFlags= */ 0, + /* notMetRequirements= */ 0, /* startTimeMs= */ currentTimeMs, /* updateTimeMs= */ currentTimeMs, action.keys.toArray(new StreamKey[0]), 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 8932140a34..f1c90e67c1 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 @@ -26,6 +26,7 @@ import static com.google.android.exoplayer2.offline.DownloadState.STATE_REMOVING import static com.google.android.exoplayer2.offline.DownloadState.STATE_RESTARTING; import static com.google.android.exoplayer2.offline.DownloadState.STATE_STOPPED; import static com.google.android.exoplayer2.offline.DownloadState.STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY; +import static com.google.android.exoplayer2.offline.DownloadState.STOP_FLAG_REQUIREMENTS_NOT_MET; import static com.google.android.exoplayer2.offline.DownloadState.STOP_FLAG_STOPPED; import android.content.Context; @@ -119,6 +120,7 @@ public final class DownloadManager { private boolean initialized; private boolean released; @DownloadState.StopFlags private int stickyStopFlags; + @Requirements.RequirementFlags private int notMetRequirements; private RequirementsWatcher requirementsWatcher; /** @@ -194,7 +196,7 @@ public final class DownloadManager { return; } requirementsWatcher.stop(); - notifyListenersRequirementsStateChange(watchRequirements(requirements)); + onRequirementsStateChanged(watchRequirements(requirements)); } /** Returns the requirements needed to be met to start downloads. */ @@ -349,7 +351,8 @@ public final class DownloadManager { } } Download download = - new Download(this, downloaderFactory, action, minRetryCount, stickyStopFlags); + new Download( + this, downloaderFactory, action, minRetryCount, stickyStopFlags, notMetRequirements); downloads.add(download); logd("Download is added", download); } @@ -401,13 +404,16 @@ public final class DownloadManager { } } - private void notifyListenersRequirementsStateChange( - @Requirements.RequirementFlags int notMetRequirements) { + private void onRequirementsStateChanged(@Requirements.RequirementFlags int notMetRequirements) { + this.notMetRequirements = notMetRequirements; logdFlags("Not met requirements are changed", notMetRequirements); for (Listener listener : listeners) { listener.onRequirementsStateChanged( DownloadManager.this, requirementsWatcher.getRequirements(), notMetRequirements); } + for (int i = 0; i < downloads.size(); i++) { + downloads.get(i).setNotMetRequirements(notMetRequirements); + } } private void loadActions() { @@ -486,7 +492,9 @@ public final class DownloadManager { @Requirements.RequirementFlags private int watchRequirements(Requirements requirements) { - requirementsWatcher = new RequirementsWatcher(context, new RequirementListener(), requirements); + RequirementsWatcher.Listener listener = + (requirementsWatcher, notMetRequirements) -> onRequirementsStateChanged(notMetRequirements); + requirementsWatcher = new RequirementsWatcher(context, listener, requirements); @Requirements.RequirementFlags int notMetRequirements = requirementsWatcher.start(); if (notMetRequirements == 0) { startDownloads(); @@ -511,17 +519,23 @@ public final class DownloadManager { @MonotonicNonNull private DownloadThread downloadThread; @MonotonicNonNull @DownloadState.FailureReason private int failureReason; @DownloadState.StopFlags private int stopFlags; + @Requirements.RequirementFlags private int notMetRequirements; private Download( DownloadManager downloadManager, DownloaderFactory downloaderFactory, DownloadAction action, int minRetryCount, - int stopFlags) { + @DownloadState.StopFlags int stopFlags, + @Requirements.RequirementFlags int notMetRequirements) { this.id = action.id; this.downloadManager = downloadManager; this.downloaderFactory = downloaderFactory; this.minRetryCount = minRetryCount; + this.notMetRequirements = notMetRequirements; + if (notMetRequirements != 0) { + stopFlags |= STOP_FLAG_REQUIREMENTS_NOT_MET; + } this.stopFlags = stopFlags; this.startTimeMs = System.currentTimeMillis(); actionQueue = new ArrayDeque<>(); @@ -579,6 +593,7 @@ public final class DownloadManager { totalBytes, failureReason, stopFlags, + notMetRequirements, startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), action.keys.toArray(new StreamKey[0]), @@ -628,6 +643,13 @@ public final class DownloadManager { } } + public void setNotMetRequirements(@Requirements.RequirementFlags int notMetRequirements) { + this.notMetRequirements = notMetRequirements; + updateStopFlags( + STOP_FLAG_REQUIREMENTS_NOT_MET, + notMetRequirements != 0 ? STOP_FLAG_REQUIREMENTS_NOT_MET : 0); + } + private void initialize(boolean restart) { DownloadAction action = actionQueue.peek(); if (action.isRemoveAction) { @@ -770,19 +792,4 @@ public final class DownloadManager { } } - private class RequirementListener implements RequirementsWatcher.Listener { - @Override - public void requirementsMet(RequirementsWatcher requirementsWatcher) { - startDownloads(); - notifyListenersRequirementsStateChange(0); - } - - @Override - public void requirementsNotMet( - RequirementsWatcher requirementsWatcher, - @Requirements.RequirementFlags int notMetRequirements) { - stopDownloads(); - notifyListenersRequirementsStateChange(notMetRequirements); - } - } } 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 7bbd078822..dd55d6ea31 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 @@ -19,6 +19,7 @@ import android.net.Uri; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.util.Assertions; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -80,12 +81,18 @@ public final class DownloadState { @Retention(RetentionPolicy.SOURCE) @IntDef( flag = true, - value = {STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY, STOP_FLAG_STOPPED}) + value = { + STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY, + STOP_FLAG_STOPPED, + STOP_FLAG_REQUIREMENTS_NOT_MET + }) public @interface StopFlags {} /** Download can't be started as the manager isn't ready. */ public static final int STOP_FLAG_DOWNLOAD_MANAGER_NOT_READY = 1; - /** All downloads are stopped by the application. */ + /** Download is stopped by the application. */ public static final int STOP_FLAG_STOPPED = 1 << 1; + /** Download is stopped as the requirements are not met. */ + public static final int STOP_FLAG_REQUIREMENTS_NOT_MET = 1 << 2; /** Returns the state string for the given state value. */ public static String getStateString(@State int state) { @@ -153,7 +160,9 @@ public final class DownloadState { */ @FailureReason public final int failureReason; /** Download stop flags. These flags stop downloading any content. */ - public final int stopFlags; + @StopFlags public final int stopFlags; + /** Not met requirements to download. */ + @Requirements.RequirementFlags public final int notMetRequirements; /* package */ DownloadState( String id, @@ -166,13 +175,17 @@ public final class DownloadState { long totalBytes, @FailureReason int failureReason, @StopFlags int stopFlags, + @Requirements.RequirementFlags int notMetRequirements, long startTimeMs, long updateTimeMs, StreamKey[] streamKeys, byte[] customMetadata) { - this.stopFlags = stopFlags; Assertions.checkState( failureReason == FAILURE_REASON_NONE ? state != STATE_FAILED : state == STATE_FAILED); + Assertions.checkState( + (stopFlags & STOP_FLAG_REQUIREMENTS_NOT_MET) == 0 + ? notMetRequirements == 0 + : notMetRequirements != 0); // TODO enable this when we start changing state immediately // Assertions.checkState(stopFlags == 0 || (state != STATE_DOWNLOADING && state != // STATE_QUEUED)); @@ -180,14 +193,16 @@ public final class DownloadState { this.type = type; this.uri = uri; this.cacheKey = cacheKey; - this.streamKeys = streamKeys; - this.customMetadata = customMetadata; this.state = state; this.downloadPercentage = downloadPercentage; this.downloadedBytes = downloadedBytes; this.totalBytes = totalBytes; this.failureReason = failureReason; + this.stopFlags = stopFlags; + this.notMetRequirements = notMetRequirements; this.startTimeMs = startTimeMs; this.updateTimeMs = updateTimeMs; + this.streamKeys = streamKeys; + this.customMetadata = customMetadata; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java index 686f19d161..dfced7c0ab 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java @@ -42,23 +42,14 @@ public final class RequirementsWatcher { * Requirements} are met. */ public interface Listener { - /** - * Called when all of the requirements are met. - * - * @param requirementsWatcher Calling instance. - */ - void requirementsMet(RequirementsWatcher requirementsWatcher); - - /** - * Called when there is at least one not met requirement and there is a change on which of the - * requirements are not met. + * Called when there is a change on the met requirements. * * @param requirementsWatcher Calling instance. * @param notMetRequirements {@link Requirements.RequirementFlags RequirementFlags} that are not * met, or 0. */ - void requirementsNotMet( + void onRequirementsStateChanged( RequirementsWatcher requirementsWatcher, @Requirements.RequirementFlags int notMetRequirements); } @@ -172,17 +163,10 @@ public final class RequirementsWatcher { private void checkRequirements() { @Requirements.RequirementFlags int notMetRequirements = requirements.getNotMetRequirements(context); - if (this.notMetRequirements == notMetRequirements) { - logd("notMetRequirements hasn't changed: " + notMetRequirements); - return; - } - this.notMetRequirements = notMetRequirements; - if (notMetRequirements == 0) { - logd("start job"); - listener.requirementsMet(this); - } else { - logd("stop job"); - listener.requirementsNotMet(this, notMetRequirements); + if (this.notMetRequirements != notMetRequirements) { + this.notMetRequirements = notMetRequirements; + logd("notMetRequirements has changed: " + notMetRequirements); + listener.onRequirementsStateChanged(this, notMetRequirements); } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java index 6f6786c068..df68f7ad34 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DefaultDownloadIndexTest.java @@ -80,13 +80,14 @@ public class DefaultDownloadIndexTest { .setDownloadedBytes(200) .setTotalBytes(400) .setFailureReason(DownloadState.FAILURE_REASON_UNKNOWN) - .setStopFlags(DownloadState.STOP_FLAG_STOPPED) + .setStopFlags(DownloadState.STOP_FLAG_REQUIREMENTS_NOT_MET) + .setNotMetRequirements(0x87654321) .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}) + .setCustomMetadata(new byte[] {0, 1, 2, 3, 7, 8, 9, 10}) .build(); downloadIndex.putDownloadState(downloadState); DownloadState readDownloadState = downloadIndex.getDownloadState(id); @@ -238,6 +239,9 @@ public class DefaultDownloadIndexTest { if (downloadState.stopFlags != that.stopFlags) { return false; } + if (downloadState.notMetRequirements != that.notMetRequirements) { + return false; + } if (!downloadState.id.equals(that.id)) { return false; } @@ -269,6 +273,7 @@ public class DefaultDownloadIndexTest { private long totalBytes; private int failureReason; private int stopFlags; + private int notMetRequirements; private long startTimeMs; private long updateTimeMs; private StreamKey[] streamKeys; @@ -341,6 +346,11 @@ public class DefaultDownloadIndexTest { return this; } + public DownloadStateBuilder setNotMetRequirements(int notMetRequirements) { + this.notMetRequirements = notMetRequirements; + return this; + } + public DownloadStateBuilder setStartTimeMs(long startTimeMs) { this.startTimeMs = startTimeMs; return this; @@ -373,6 +383,7 @@ public class DefaultDownloadIndexTest { totalBytes, failureReason, stopFlags, + notMetRequirements, startTimeMs, updateTimeMs, streamKeys,