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 95192f6f74..357233d283 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 @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.demo; import android.app.Notification; -import android.util.Pair; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.TaskState; import com.google.android.exoplayer2.offline.DownloadService; @@ -27,7 +26,6 @@ import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction; import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction; import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction; import com.google.android.exoplayer2.ui.DownloadNotificationUtil; -import com.google.android.exoplayer2.util.ErrorMessageProvider; import com.google.android.exoplayer2.util.NotificationUtil; import com.google.android.exoplayer2.util.Util; @@ -76,32 +74,39 @@ public class DemoDownloadService extends DownloadService { @Override protected Notification getForegroundNotification(TaskState[] taskStates) { - return DownloadNotificationUtil.createProgressNotification( - taskStates, + return DownloadNotificationUtil.buildProgressNotification( /* context= */ this, R.drawable.exo_controls_play, CHANNEL_ID, /* contentIntent= */ null, - /* message= */ null); + /* message= */ null, + taskStates); } @Override protected void onTaskStateChanged(TaskState taskState) { + if (taskState.action.isRemoveAction) { + return; + } + Notification notification = null; + if (taskState.state == TaskState.STATE_COMPLETED) { + notification = + DownloadNotificationUtil.buildDownloadCompletedNotification( + /* context= */ this, + R.drawable.exo_controls_play, + CHANNEL_ID, + /* contentIntent= */ null, + taskState.action.data); + } else if (taskState.state == TaskState.STATE_FAILED) { + notification = + DownloadNotificationUtil.buildDownloadFailedNotification( + /* context= */ this, + R.drawable.exo_controls_play, + CHANNEL_ID, + /* contentIntent= */ null, + taskState.action.data); + } int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + taskState.taskId; - Notification downloadNotification = - DownloadNotificationUtil.createDownloadFinishedNotification( - taskState, - /* context= */ this, - R.drawable.exo_controls_play, - CHANNEL_ID, - /* contentIntent= */ null, - taskState.action.data, - new ErrorMessageProvider() { - @Override - public Pair getErrorMessage(Throwable throwable) { - return new Pair<>(0, throwable.getLocalizedMessage()); - } - }); - NotificationUtil.setNotification(/* context= */ this, notificationId, downloadNotification); + NotificationUtil.setNotification(this, notificationId, notification); } } 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 542b6cdbb6..01548958dc 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 @@ -16,8 +16,8 @@ package com.google.android.exoplayer2.offline; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_CANCELED; -import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_ENDED; -import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_ERROR; +import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_COMPLETED; +import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_FAILED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_QUEUED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_STARTED; @@ -487,23 +487,23 @@ public final class DownloadManager { * *
      *                    -> canceled
-     * queued <-> started -> ended
-     *                    -> error
+     * queued <-> started -> completed
+     *                    -> failed
      * 
*/ @Retention(RetentionPolicy.SOURCE) - @IntDef({STATE_QUEUED, STATE_STARTED, STATE_ENDED, STATE_CANCELED, STATE_ERROR}) + @IntDef({STATE_QUEUED, STATE_STARTED, STATE_COMPLETED, STATE_CANCELED, STATE_FAILED}) public @interface State {} /** The task is waiting to be started. */ public static final int STATE_QUEUED = 0; /** The task is currently started. */ public static final int STATE_STARTED = 1; /** The task completed. */ - public static final int STATE_ENDED = 2; + public static final int STATE_COMPLETED = 2; /** The task was canceled. */ public static final int STATE_CANCELED = 3; /** The task failed. */ - public static final int STATE_ERROR = 4; + public static final int STATE_FAILED = 4; /** Returns the state string for the given state value. */ public static String getStateString(@State int state) { @@ -512,12 +512,12 @@ public final class DownloadManager { return "QUEUED"; case STATE_STARTED: return "STARTED"; - case STATE_ENDED: - return "ENDED"; + case STATE_COMPLETED: + return "COMPLETED"; case STATE_CANCELED: return "CANCELED"; - case STATE_ERROR: - return "ERROR"; + case STATE_FAILED: + return "FAILED"; default: throw new IllegalStateException(); } @@ -538,7 +538,7 @@ public final class DownloadManager { /** The total number of downloaded bytes. */ public final long downloadedBytes; - /** If {@link #state} is {@link #STATE_ERROR} then this is the cause, otherwise null. */ + /** If {@link #state} is {@link #STATE_FAILED} then this is the cause, otherwise null. */ public final Throwable error; private TaskState( @@ -566,24 +566,24 @@ public final class DownloadManager { *

Transition map (vertical states are source states): * *

-     *             +------+-------+-----+-----------+-----------+--------+--------+-----+
-     *             |queued|started|ended|q_canceling|s_canceling|canceled|stopping|error|
-     * +-----------+------+-------+-----+-----------+-----------+--------+--------+-----+
-     * |queued     |      |   X   |     |     X     |           |        |        |     |
-     * |started    |      |       |  X  |           |     X     |        |   X    |  X  |
-     * |q_canceling|      |       |     |           |           |   X    |        |     |
-     * |s_canceling|      |       |     |           |           |   X    |        |     |
-     * |stopping   |   X  |       |     |           |           |        |        |     |
-     * +-----------+------+-------+-----+-----------+-----------+--------+--------+-----+
+     *             +------+-------+---------+-----------+-----------+--------+--------+------+
+     *             |queued|started|completed|q_canceling|s_canceling|canceled|stopping|failed|
+     * +-----------+------+-------+---------+-----------+-----------+--------+--------+------+
+     * |queued     |      |   X   |         |     X     |           |        |        |      |
+     * |started    |      |       |    X    |           |     X     |        |   X    |   X  |
+     * |q_canceling|      |       |         |           |           |   X    |        |      |
+     * |s_canceling|      |       |         |           |           |   X    |        |      |
+     * |stopping   |   X  |       |         |           |           |        |        |      |
+     * +-----------+------+-------+---------+-----------+-----------+--------+--------+------+
      * 
*/ @Retention(RetentionPolicy.SOURCE) @IntDef({ STATE_QUEUED, STATE_STARTED, - STATE_ENDED, + STATE_COMPLETED, STATE_CANCELED, - STATE_ERROR, + STATE_FAILED, STATE_QUEUED_CANCELING, STATE_STARTED_CANCELING, STATE_STARTED_STOPPING @@ -622,8 +622,8 @@ public final class DownloadManager { /** Returns whether the task is finished. */ public boolean isFinished() { - return currentState == STATE_ERROR - || currentState == STATE_ENDED + return currentState == STATE_FAILED + || currentState == STATE_COMPLETED || currentState == STATE_CANCELED; } @@ -778,7 +778,9 @@ public final class DownloadManager { @Override public void run() { if (changeStateAndNotify( - STATE_STARTED, finalError != null ? STATE_ERROR : STATE_ENDED, finalError) + STATE_STARTED, + finalError != null ? STATE_FAILED : STATE_COMPLETED, + finalError) || changeStateAndNotify(STATE_STARTED_CANCELING, STATE_CANCELED) || changeStateAndNotify(STATE_STARTED_STOPPING, STATE_QUEUED)) { return; 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 e11d7a367d..2e15f81ad5 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 @@ -102,21 +102,21 @@ public class DownloadManagerTest { for (int i = 0; i <= MIN_RETRY_COUNT; i++) { fakeDownloader.assertStarted(MAX_RETRY_DELAY).unblock(); } - downloadAction.assertError(); + downloadAction.assertFailed(); testDownloadListener.clearDownloadError(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @Test - public void testDownloadNoRetryWhenCancelled() throws Throwable { + public void testDownloadNoRetryWhenCanceled() throws Throwable { FakeDownloadAction downloadAction = createDownloadAction("media 1").ignoreInterrupts(); downloadAction.getFakeDownloader().enableDownloadIOException = true; downloadAction.post().assertStarted(); FakeDownloadAction removeAction = createRemoveAction("media 1").post(); - downloadAction.unblock().assertCancelled(); + downloadAction.unblock().assertCanceled(); removeAction.unblock(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); @@ -135,7 +135,7 @@ public class DownloadManagerTest { } fakeDownloader.unblock(); } - downloadAction.assertEnded(); + downloadAction.assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -156,7 +156,7 @@ public class DownloadManagerTest { } fakeDownloader.unblock(); } - downloadAction.assertEnded(); + downloadAction.assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -207,13 +207,13 @@ public class DownloadManagerTest { removeAction1.post().assertDoesNotStart(); // downloadAction2 finishes but it isn't enough to start removeAction1. - downloadAction2.unblock().assertCancelled(); + downloadAction2.unblock().assertCanceled(); removeAction1.assertDoesNotStart(); // downloadAction3 is post to DownloadManager but it waits for removeAction1 to finish. downloadAction3.post().assertDoesNotStart(); // When downloadAction1 finishes, removeAction1 starts. - downloadAction1.unblock().assertCancelled(); + downloadAction1.unblock().assertCanceled(); removeAction1.assertStarted(); // downloadAction3 still waits removeAction1 downloadAction3.assertDoesNotStart(); @@ -221,9 +221,9 @@ public class DownloadManagerTest { // removeAction2 is posted. removeAction1 and downloadAction3 is canceled so removeAction2 // starts immediately. removeAction2.post(); - removeAction1.assertCancelled(); - downloadAction3.assertCancelled(); - removeAction2.assertStarted().unblock().assertEnded(); + removeAction1.assertCanceled(); + downloadAction3.assertCanceled(); + removeAction2.assertStarted().unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -237,10 +237,10 @@ public class DownloadManagerTest { removeAction2.post().assertDoesNotStart(); removeAction3.post().assertDoesNotStart(); - removeAction2.assertCancelled(); + removeAction2.assertCanceled(); - removeAction1.unblock().assertCancelled(); - removeAction3.assertStarted().unblock().assertEnded(); + removeAction1.unblock().assertCanceled(); + removeAction3.assertStarted().unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -272,11 +272,11 @@ public class DownloadManagerTest { downloadAction1.post().assertDoesNotStart(); downloadAction2.post().assertDoesNotStart(); - removeAction.unblock().assertEnded(); + removeAction.unblock().assertCompleted(); downloadAction1.assertStarted(); downloadAction2.assertStarted(); - downloadAction1.unblock().assertEnded(); - downloadAction2.unblock().assertEnded(); + downloadAction1.unblock().assertCompleted(); + downloadAction2.unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -291,11 +291,11 @@ public class DownloadManagerTest { downloadAction1.post().assertDoesNotStart(); downloadAction2.post().assertDoesNotStart(); - removeAction.unblock().assertEnded(); + removeAction.unblock().assertCompleted(); downloadAction1.assertStarted(); downloadAction2.assertStarted(); - downloadAction1.unblock().assertEnded(); - downloadAction2.unblock().assertEnded(); + downloadAction1.unblock().assertCompleted(); + downloadAction2.unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -310,11 +310,11 @@ public class DownloadManagerTest { removeAction1.post().assertDoesNotStart(); removeAction2.post().assertStarted(); - downloadAction.unblock().assertCancelled(); - removeAction2.unblock().assertEnded(); + downloadAction.unblock().assertCanceled(); + removeAction2.unblock().assertCompleted(); removeAction1.assertStarted(); - removeAction1.unblock().assertEnded(); + removeAction1.unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -342,14 +342,14 @@ public class DownloadManagerTest { download1Action.assertStopped(); // remove actions aren't stopped. - remove2Action.unblock().assertEnded(); + remove2Action.unblock().assertCompleted(); // Although remove2Action is finished, download2Action doesn't start. download2Action.assertDoesNotStart(); // When a new remove action is added, it cancels stopped download actions with the same media. remove1Action.post(); - download1Action.assertCancelled(); - remove1Action.assertStarted().unblock().assertEnded(); + download1Action.assertCanceled(); + remove1Action.assertStarted().unblock().assertCompleted(); // New download actions can be added but they don't start. download3Action.post().assertDoesNotStart(); @@ -362,8 +362,8 @@ public class DownloadManagerTest { } }); - download2Action.assertStarted().unblock().assertEnded(); - download3Action.assertStarted().unblock().assertEnded(); + download2Action.assertStarted().unblock().assertCompleted(); + download3Action.assertStarted().unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -456,7 +456,7 @@ public class DownloadManagerTest { } private void doTestActionRuns(FakeDownloadAction action) throws Throwable { - action.post().assertStarted().unblock().assertEnded(); + action.post().assertStarted().unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -468,7 +468,7 @@ public class DownloadManagerTest { action1.unblock(); action2.assertStarted(); - action2.unblock().assertEnded(); + action2.unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -476,8 +476,8 @@ public class DownloadManagerTest { throws Throwable { action1.post().assertStarted(); action2.post().assertStarted(); - action1.unblock().assertEnded(); - action2.unblock().assertEnded(); + action1.unblock().assertCompleted(); + action2.unblock().assertCompleted(); testDownloadListener.blockUntilTasksCompleteAndThrowAnyDownloadError(); } @@ -504,7 +504,7 @@ public class DownloadManagerTest { @Override public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) { - if (taskState.state == TaskState.STATE_ERROR && downloadError == null) { + if (taskState.state == TaskState.STATE_FAILED && downloadError == null) { downloadError = taskState.error; } ((FakeDownloadAction) taskState.action).onStateChange(taskState.state); @@ -583,15 +583,15 @@ public class DownloadManagerTest { return assertState(TaskState.STATE_STARTED); } - private FakeDownloadAction assertEnded() { - return assertState(TaskState.STATE_ENDED); + private FakeDownloadAction assertCompleted() { + return assertState(TaskState.STATE_COMPLETED); } - private FakeDownloadAction assertError() { - return assertState(TaskState.STATE_ERROR); + private FakeDownloadAction assertFailed() { + return assertState(TaskState.STATE_FAILED); } - private FakeDownloadAction assertCancelled() { + private FakeDownloadAction assertCanceled() { return assertState(TaskState.STATE_CANCELED); } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/TestDownloadListener.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/TestDownloadListener.java index 7300776855..667634cb8a 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/TestDownloadListener.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/TestDownloadListener.java @@ -41,7 +41,7 @@ import java.util.concurrent.TimeUnit; @Override public void onTaskStateChanged( DownloadManager downloadManager, DownloadManager.TaskState taskState) { - if (taskState.state == DownloadManager.TaskState.STATE_ERROR && downloadError == null) { + if (taskState.state == DownloadManager.TaskState.STATE_FAILED && downloadError == null) { downloadError = taskState.error; } } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java index 30698b6d0e..0a841fa38f 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java @@ -23,11 +23,9 @@ import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.app.NotificationCompat; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.TaskState; -import com.google.android.exoplayer2.util.ErrorMessageProvider; -/** Helper class to create notifications for downloads using {@link DownloadManager}. */ +/** Helper for creating download notifications. */ public final class DownloadNotificationUtil { private static final @StringRes int NULL_STRING_ID = 0; @@ -35,24 +33,24 @@ public final class DownloadNotificationUtil { private DownloadNotificationUtil() {} /** - * Returns a progress notification for the given {@link TaskState}s. + * Returns a progress notification for the given task states. * - * @param taskStates States of the downloads. - * @param context Used to access resources. + * @param context A context for accessing resources. * @param smallIcon A small icon for the notification. * @param channelId The id of the notification channel to use. Only required for API level 26 and * above. * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. - * @return A progress notification for the given {@link TaskState}s. + * @param taskStates The task states. + * @return The notification. */ - public static @Nullable Notification createProgressNotification( - TaskState[] taskStates, + public static Notification buildProgressNotification( Context context, @DrawableRes int smallIcon, String channelId, @Nullable PendingIntent contentIntent, - @Nullable String message) { + @Nullable String message, + TaskState[] taskStates) { float totalPercentage = 0; int downloadTaskCount = 0; boolean allDownloadPercentagesUnknown = true; @@ -75,7 +73,7 @@ public final class DownloadNotificationUtil { ? R.string.exo_download_downloading : (taskStates.length > 0 ? R.string.exo_download_removing : NULL_STRING_ID); NotificationCompat.Builder notificationBuilder = - createNotificationBuilder( + newNotificationBuilder( context, smallIcon, channelId, contentIntent, message, titleStringId); int progress = haveDownloadTasks ? (int) (totalPercentage / downloadTaskCount) : 0; @@ -88,51 +86,52 @@ public final class DownloadNotificationUtil { } /** - * Returns a notification for a {@link TaskState} which is in either {@link TaskState#STATE_ENDED} - * or {@link TaskState#STATE_ERROR} states. Returns null if it's some other state or it's state of - * a remove action. + * Returns a notification for a completed download. * - * @param taskState State of the download. - * @param context Used to access resources. + * @param context A context for accessing resources. * @param smallIcon A small icon for the notifications. * @param channelId The id of the notification channel to use. Only required for API level 26 and * above. * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. - * @param errorMessageProvider An optional {@link ErrorMessageProvider} for translating download - * errors into readable error messages. If not null and there is a download error then the - * error message is displayed instead of {@code message}. - * @return A notification for a {@link TaskState} which is in either {@link TaskState#STATE_ENDED} - * or {@link TaskState#STATE_ERROR} states. Returns null if it's some other state or it's - * state of a remove action. + * @return The notification. */ - public static @Nullable Notification createDownloadFinishedNotification( - TaskState taskState, + public static Notification buildDownloadCompletedNotification( Context context, @DrawableRes int smallIcon, String channelId, @Nullable PendingIntent contentIntent, - @Nullable String message, - @Nullable ErrorMessageProvider errorMessageProvider) { - if (taskState.action.isRemoveAction - || (taskState.state != TaskState.STATE_ENDED && taskState.state != TaskState.STATE_ERROR)) { - return null; - } - if (taskState.error != null && errorMessageProvider != null) { - message = errorMessageProvider.getErrorMessage(taskState.error).second; - } - @StringRes - int titleStringId = - taskState.state == TaskState.STATE_ENDED - ? R.string.exo_download_completed - : R.string.exo_download_failed; - NotificationCompat.Builder notificationBuilder = - createNotificationBuilder( - context, smallIcon, channelId, contentIntent, message, titleStringId); - return notificationBuilder.build(); + @Nullable String message) { + int titleStringId = R.string.exo_download_completed; + return newNotificationBuilder( + context, smallIcon, channelId, contentIntent, message, titleStringId) + .build(); } - private static NotificationCompat.Builder createNotificationBuilder( + /** + * Returns a notification for a failed download. + * + * @param context A context for accessing resources. + * @param smallIcon A small icon for the notifications. + * @param channelId The id of the notification channel to use. Only required for API level 26 and + * above. + * @param contentIntent An optional content intent to send when the notification is clicked. + * @param message An optional message to display on the notification. + * @return The notification. + */ + public static Notification buildDownloadFailedNotification( + Context context, + @DrawableRes int smallIcon, + String channelId, + @Nullable PendingIntent contentIntent, + @Nullable String message) { + @StringRes int titleStringId = R.string.exo_download_failed; + return newNotificationBuilder( + context, smallIcon, channelId, contentIntent, message, titleStringId) + .build(); + } + + private static NotificationCompat.Builder newNotificationBuilder( Context context, @DrawableRes int smallIcon, String channelId,