From 03e28d4689ea17d09bbe7209428d27280ac33000 Mon Sep 17 00:00:00 2001 From: eguven Date: Mon, 18 Feb 2019 15:55:27 +0000 Subject: [PATCH] Support stopping/starting single downloads Also added intent actions to stop/start one or all downloads. Issue: #4433 Issue: #4860 PiperOrigin-RevId: 234481515 --- .../exoplayer2/offline/DownloadManager.java | 61 ++++++++++++++--- .../exoplayer2/offline/DownloadService.java | 65 +++++++++++++++++++ .../offline/DownloadManagerTest.java | 58 +++++++++++++++++ 3 files changed, 176 insertions(+), 8 deletions(-) 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 cd5f0dc6cf..3ccba10807 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 @@ -260,7 +260,7 @@ public final class DownloadManager { /** Signals all downloads to stop. Call {@link #startDownloads()} to let them to be started. */ public void stopDownloads() { - stopDownloads(0); + stopDownloads(/* manualStopReason= */ 0); } /** @@ -277,6 +277,45 @@ public final class DownloadManager { } } + /** + * Clears {@link DownloadState#STOP_FLAG_MANUAL} flag of the download with the {@code id}. + * Download is started if the requirements are met. + * + * @param id The unique content id of the download to be started. + */ + public void startDownload(String id) { + Download download = getDownload(id); + if (download != null) { + logd("download is started manually", download); + download.clearManualStopReason(); + } + } + + /** + * Signals the download with the {@code id} to stop. Call {@link #startDownload(String)} to let it + * to be started. + * + * @param id The unique content id of the download to be stopped. + */ + public void stopDownload(String id) { + stopDownload(id, /* manualStopReason= */ 0); + } + + /** + * Signals the download with the {@code id} to stop. Call {@link #startDownload(String)} to let it + * to be started. + * + * @param id The unique content id of the download to be stopped. + * @param manualStopReason An application defined stop reason. + */ + public void stopDownload(String id, int manualStopReason) { + Download download = getDownload(id); + if (download != null) { + logd("download is stopped manually", download); + download.setManualStopReason(manualStopReason); + } + } + /** * Handles the given action. * @@ -307,13 +346,8 @@ public final class DownloadManager { @Nullable public DownloadState getDownloadState(String id) { Assertions.checkState(!released); - for (int i = 0; i < downloads.size(); i++) { - Download download = downloads.get(i); - if (download.getId().equals(id)) { - return download.getDownloadState(); - } - } - return null; + Download download = getDownload(id); + return download != null ? download.getDownloadState() : null; } /** Returns the states of all current downloads. */ @@ -418,6 +452,17 @@ public final class DownloadManager { } } + @Nullable + private Download getDownload(String id) { + for (int i = 0; i < downloads.size(); i++) { + Download download = downloads.get(i); + if (download.getId().equals(id)) { + return download; + } + } + return null; + } + private void loadActions() { fileIOHandler.post( () -> { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index 305620d5f3..2db33cb59c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -43,6 +43,14 @@ public abstract class DownloadService extends Service { /** Starts a download service, adding a new {@link DownloadAction} to be executed. */ public static final String ACTION_ADD = "com.google.android.exoplayer.downloadService.action.ADD"; + /** Stops one or all downloads. */ + public static final String ACTION_STOP = + "com.google.android.exoplayer.downloadService.action.STOP"; + + /** Starts one or all downloads. */ + public static final String ACTION_START = + "com.google.android.exoplayer.downloadService.action.START"; + /** Like {@link #ACTION_INIT}, but with {@link #KEY_FOREGROUND} implicitly set to true. */ private static final String ACTION_RESTART = "com.google.android.exoplayer.downloadService.action.RESTART"; @@ -50,6 +58,12 @@ public abstract class DownloadService extends Service { /** Key for the {@link DownloadAction} in an {@link #ACTION_ADD} intent. */ public static final String KEY_DOWNLOAD_ACTION = "download_action"; + /** Key for content id in an {@link #ACTION_STOP} or {@link #ACTION_START} intent. */ + public static final String KEY_CONTENT_ID = "content_id"; + + /** Key for manual stop reason in an {@link #ACTION_STOP} intent. */ + public static final String KEY_STOP_REASON = "stop_reason"; + /** Invalid foreground notification id which can be used to run the service in the background. */ public static final int FOREGROUND_NOTIFICATION_ID_NONE = 0; @@ -165,6 +179,40 @@ public abstract class DownloadService extends Service { .putExtra(KEY_FOREGROUND, foreground); } + /** + * Builds an {@link Intent} for stopping the download with the {@code id}. If {@code id} is null, + * stops all downloads. + * + * @param context A {@link Context}. + * @param clazz The concrete download service being targeted by the intent. + * @param id The content id, or null if all downloads should be stopped. + * @param manualStopReason An application defined stop reason. + * @return Created Intent. + */ + public static Intent buildStopDownloadIntent( + Context context, + Class clazz, + @Nullable String id, + int manualStopReason) { + return getIntent(context, clazz, ACTION_STOP) + .putExtra(KEY_CONTENT_ID, id) + .putExtra(KEY_STOP_REASON, manualStopReason); + } + + /** + * Builds an {@link Intent} for starting the download with the {@code id}. If {@code id} is null, + * starts all downloads. + * + * @param context A {@link Context}. + * @param clazz The concrete download service being targeted by the intent. + * @param id The content id, or null if all downloads should be started. + * @return Created Intent. + */ + public static Intent buildStartDownloadIntent( + Context context, Class clazz, @Nullable String id) { + return getIntent(context, clazz, ACTION_START).putExtra(KEY_CONTENT_ID, id); + } + /** * Starts the service, adding an action to be executed. * @@ -263,6 +311,23 @@ public abstract class DownloadService extends Service { } } break; + case ACTION_STOP: + String stopDownloadId = intent.getStringExtra(KEY_CONTENT_ID); + int stopReason = intent.getIntExtra(KEY_STOP_REASON, 0); + if (stopDownloadId == null) { + downloadManager.stopDownloads(stopReason); + } else { + downloadManager.stopDownload(stopDownloadId, stopReason); + } + break; + case ACTION_START: + String startDownloadId = intent.getStringExtra(KEY_CONTENT_ID); + if (startDownloadId == null) { + downloadManager.startDownloads(); + } else { + downloadManager.startDownload(startDownloadId); + } + break; default: Log.e(TAG, "Ignoring unrecognized action: " + intentAction); break; 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 a4e63a1c78..74b9c4dfce 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 @@ -403,6 +403,64 @@ public class DownloadManagerTest { downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } + @Test + public void stopAndResumeSingleDownload() throws Throwable { + DownloadRunner runner = new DownloadRunner(uri1).postDownloadAction(); + TaskWrapper task = runner.getTask(); + + task.assertDownloading(); + + runOnMainThread(() -> downloadManager.stopDownload(task.taskId)); + + task.assertStopped(); + + runOnMainThread(() -> downloadManager.startDownload(task.taskId)); + + runner.getDownloader(1).assertStarted().unblock(); + + downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); + } + + @Test + public void stoppedDownloadCanBeCancelled() throws Throwable { + DownloadRunner runner = new DownloadRunner(uri1).postDownloadAction(); + TaskWrapper task = runner.getTask(); + + task.assertDownloading(); + + runOnMainThread(() -> downloadManager.stopDownload(task.taskId)); + + task.assertStopped(); + + runner.postRemoveAction(); + runner.getDownloader(1).assertStarted().unblock(); + task.assertRemoved(); + + downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); + } + + @Test + public void stoppedSingleDownload_doesNotAffectOthers() throws Throwable { + DownloadRunner runner1 = new DownloadRunner(uri1); + DownloadRunner runner2 = new DownloadRunner(uri2); + DownloadRunner runner3 = new DownloadRunner(uri3); + + runner1.postDownloadAction().getTask().assertDownloading(); + runner2.postRemoveAction().getTask().assertRemoving(); + + runOnMainThread(() -> downloadManager.stopDownload(runner1.getTask().taskId)); + + runner1.getTask().assertStopped(); + + // Other downloads aren't affected. + runner2.getDownloader(0).unblock().assertReleased(); + + // New download actions can be added and they start. + runner3.postDownloadAction().getDownloader(0).assertStarted().unblock(); + + downloadManagerListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); + } + private void setUpDownloadManager(final int maxActiveDownloadTasks) throws Exception { if (downloadManager != null) { releaseDownloadManager();