Clean up offline class names

This change is intended to resolve overloading of "Download",
where a DownloadTask could be an actual download task, or a
remove task.

Also cleaned up some documentation.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=194815058
This commit is contained in:
olly 2018-04-30 11:17:04 -07:00 committed by Oliver Woodman
parent c68e00e28f
commit 7723f5bdf1
8 changed files with 187 additions and 195 deletions

View File

@ -18,7 +18,7 @@ package com.google.android.exoplayer2.demo;
import android.app.Notification; import android.app.Notification;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.ProgressiveDownloadAction; import com.google.android.exoplayer2.offline.ProgressiveDownloadAction;
@ -75,21 +75,21 @@ public class DemoDownloadService extends DownloadService {
} }
@Override @Override
protected Notification getForegroundNotification(DownloadState[] downloadStates) { protected Notification getForegroundNotification(TaskState[] taskStates) {
return DownloadNotificationUtil.createProgressNotification( return DownloadNotificationUtil.createProgressNotification(
downloadStates, this, R.drawable.exo_controls_play, CHANNEL_ID, null); taskStates, this, R.drawable.exo_controls_play, CHANNEL_ID, null);
} }
@Override @Override
protected void onStateChange(DownloadState downloadState) { protected void onTaskStateChanged(TaskState taskState) {
int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + downloadState.taskId; int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + taskState.taskId;
Notification downloadNotification = Notification downloadNotification =
DownloadNotificationUtil.createDownloadFinishedNotification( DownloadNotificationUtil.createDownloadFinishedNotification(
downloadState, taskState,
this, this,
R.drawable.exo_controls_play, R.drawable.exo_controls_play,
CHANNEL_ID, CHANNEL_ID,
downloadState.downloadAction.data, taskState.action.data,
new ErrorMessageProvider<Throwable>() { new ErrorMessageProvider<Throwable>() {
@Override @Override
public Pair<Integer, String> getErrorMessage(Throwable throwable) { public Pair<Integer, String> getErrorMessage(Throwable throwable) {

View File

@ -43,7 +43,7 @@ public abstract class DownloadAction {
} }
/** /**
* Deserializes a {@link DownloadAction} from the {@code input}. * Deserializes an action from the {@code input}.
* *
* @param version Version of the data. * @param version Version of the data.
* @param input DataInputStream to read data from. * @param input DataInputStream to read data from.
@ -55,17 +55,17 @@ public abstract class DownloadAction {
} }
/** /**
* Deserializes one {@code action} which was serialized by {@link * Deserializes one action that was serialized with {@link #serializeToStream(DownloadAction,
* #serializeToStream(DownloadAction, OutputStream)} from the {@code input} using one of the * OutputStream)} from the {@code input}, using the {@link Deserializer}s that supports the
* {@link Deserializer}s which supports the type of the action. * action's type.
* *
* <p>The caller is responsible for closing the given {@link InputStream}. * <p>The caller is responsible for closing the given {@link InputStream}.
* *
* @param deserializers Array of {@link Deserializer}s to deserialize a {@link DownloadAction}. * @param deserializers {@link Deserializer}s for supported actions.
* @param input Input stream to read serialized data. * @param input Input stream to read serialized data.
* @return The deserialized {@link DownloadAction}. * @return The deserialized action.
* @throws IOException If there is an IO error from {@code input} or the action type isn't * @throws IOException If there is an IO error reading from {@code input}, or if the action type
* supported by any of the {@code deserializers}. * isn't supported by any of the {@code deserializers}.
*/ */
public static DownloadAction deserializeFromStream( public static DownloadAction deserializeFromStream(
Deserializer[] deserializers, InputStream input) throws IOException { Deserializer[] deserializers, InputStream input) throws IOException {
@ -79,10 +79,10 @@ public abstract class DownloadAction {
* *
* <p>The caller is responsible for closing the given {@link InputStream}. * <p>The caller is responsible for closing the given {@link InputStream}.
* *
* @param deserializers Array of {@link Deserializer}s to deserialize a {@link DownloadAction}. * @param deserializers {@link Deserializer}s for supported actions.
* @param input Input stream to read serialized data. * @param input Input stream to read serialized data.
* @param version Master version of the serialization. See {@link DownloadAction#MASTER_VERSION}. * @param version Master version of the serialization. See {@link DownloadAction#MASTER_VERSION}.
* @return The deserialized {@link DownloadAction}. * @return The deserialized action.
* @throws IOException If there is an IO error from {@code input}. * @throws IOException If there is an IO error from {@code input}.
* @throws DownloadException If the action type isn't supported by any of the {@code * @throws DownloadException If the action type isn't supported by any of the {@code
* deserializers}. * deserializers}.

View File

@ -15,17 +15,18 @@
*/ */
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import static com.google.android.exoplayer2.offline.DownloadManager.DownloadState.STATE_CANCELED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_CANCELED;
import static com.google.android.exoplayer2.offline.DownloadManager.DownloadState.STATE_ENDED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_ENDED;
import static com.google.android.exoplayer2.offline.DownloadManager.DownloadState.STATE_ERROR; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_ERROR;
import static com.google.android.exoplayer2.offline.DownloadManager.DownloadState.STATE_QUEUED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_QUEUED;
import static com.google.android.exoplayer2.offline.DownloadManager.DownloadState.STATE_STARTED; import static com.google.android.exoplayer2.offline.DownloadManager.TaskState.STATE_STARTED;
import android.os.ConditionVariable; import android.os.ConditionVariable;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadAction.Deserializer; import com.google.android.exoplayer2.offline.DownloadAction.Deserializer;
@ -43,24 +44,24 @@ import java.util.concurrent.CopyOnWriteArraySet;
/** /**
* Manages multiple stream download and remove requests. * Manages multiple stream download and remove requests.
* *
* <p>By default downloads are stopped. Call {@link #startDownloads()} to start downloads. * <p>A download manager instance must be accessed only from the thread that created it, unless that
* thread does not have a {@link Looper}. In that case, it must be accessed only from the
* application's main thread. Registered listeners will be called on the same thread.
* *
* <p>WARNING: Methods of this class must be called only on the main thread of the application. * <p>By default download tasks are stopped, so {@link #startDownloads()} must be called to start
* them.
*/ */
public final class DownloadManager { public final class DownloadManager {
/** /** Listener for {@link DownloadManager} events. */
* Listener for download events. Listener methods are called on the main thread of the
* application.
*/
public interface DownloadListener { public interface DownloadListener {
/** /**
* Called on download state change. * Called when the state of a task changes.
* *
* @param downloadManager The reporting instance. * @param downloadManager The reporting instance.
* @param downloadState The download task. * @param taskState The state of the task.
*/ */
void onStateChange(DownloadManager downloadManager, DownloadState downloadState); void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState);
/** /**
* Called when there is no active task left. * Called when there is no active task left.
@ -70,9 +71,9 @@ public final class DownloadManager {
void onIdle(DownloadManager downloadManager); void onIdle(DownloadManager downloadManager);
} }
/** The default maximum number of simultaneous downloads. */ /** The default maximum number of simultaneous download tasks. */
public static final int DEFAULT_MAX_SIMULTANEOUS_DOWNLOADS = 1; public static final int DEFAULT_MAX_SIMULTANEOUS_DOWNLOADS = 1;
/** The default minimum number of times the downloads must be retried before failing. */ /** The default minimum number of times a task must be retried before failing. */
public static final int DEFAULT_MIN_RETRY_COUNT = 5; public static final int DEFAULT_MIN_RETRY_COUNT = 5;
private static final String TAG = "DownloadManager"; private static final String TAG = "DownloadManager";
@ -83,8 +84,8 @@ public final class DownloadManager {
private final int minRetryCount; private final int minRetryCount;
private final ActionFile actionFile; private final ActionFile actionFile;
private final DownloadAction.Deserializer[] deserializers; private final DownloadAction.Deserializer[] deserializers;
private final ArrayList<DownloadTask> tasks; private final ArrayList<Task> tasks;
private final ArrayList<DownloadTask> activeDownloadTasks; private final ArrayList<Task> activeDownloadTasks;
private final Handler handler; private final Handler handler;
private final HandlerThread fileIOThread; private final HandlerThread fileIOThread;
private final Handler fileIOHandler; private final Handler fileIOHandler;
@ -140,8 +141,8 @@ public final class DownloadManager {
* *
* @param constructorHelper A {@link DownloaderConstructorHelper} to create {@link Downloader}s * @param constructorHelper A {@link DownloaderConstructorHelper} to create {@link Downloader}s
* for downloading data. * for downloading data.
* @param maxSimultaneousDownloads The maximum number of simultaneous downloads. * @param maxSimultaneousDownloads The maximum number of simultaneous download tasks.
* @param minRetryCount The minimum number of times the downloads must be retried before failing. * @param minRetryCount The minimum number of times a task must be retried before failing.
* @param actionFile The file in which active actions are saved. * @param actionFile The file in which active actions are saved.
* @param deserializers Used to deserialize {@link DownloadAction}s. * @param deserializers Used to deserialize {@link DownloadAction}s.
*/ */
@ -151,8 +152,7 @@ public final class DownloadManager {
int minRetryCount, int minRetryCount,
File actionFile, File actionFile,
Deserializer... deserializers) { Deserializer... deserializers) {
Assertions.checkArgument( Assertions.checkArgument(deserializers.length > 0, "At least one Deserializer is required.");
deserializers.length > 0, "At least one Deserializer should be given.");
this.downloaderConstructorHelper = constructorHelper; this.downloaderConstructorHelper = constructorHelper;
this.maxActiveDownloadTasks = maxSimultaneousDownloads; this.maxActiveDownloadTasks = maxSimultaneousDownloads;
@ -177,7 +177,7 @@ public final class DownloadManager {
listeners = new CopyOnWriteArraySet<>(); listeners = new CopyOnWriteArraySet<>();
loadActions(); loadActions();
logd("DownloadManager is created"); logd("Created");
} }
/** /**
@ -198,7 +198,7 @@ public final class DownloadManager {
}); });
fileIOFinishedCondition.block(); fileIOFinishedCondition.block();
fileIOThread.quit(); fileIOThread.quit();
logd("DownloadManager is released"); logd("Released");
} }
/** Stops all of the download tasks. Call {@link #startDownloads()} to restart tasks. */ /** Stops all of the download tasks. Call {@link #startDownloads()} to restart tasks. */
@ -240,12 +240,12 @@ public final class DownloadManager {
} }
/** /**
* Deserializes one {@link DownloadAction} from {@code actionData} and calls {@link * Deserializes an action from {@code actionData}, and calls {@link
* #handleAction(DownloadAction)}. * #handleAction(DownloadAction)}.
* *
* @param actionData Serialized {@link DownloadAction} data. * @param actionData Serialized version of the action to be executed.
* @return The task id. * @return The id of the newly created task.
* @throws IOException If an error occurs during handling action. * @throws IOException If an error occurs deserializing the action.
*/ */
public int handleAction(byte[] actionData) throws IOException { public int handleAction(byte[] actionData) throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(actionData); ByteArrayInputStream input = new ByteArrayInputStream(actionData);
@ -254,41 +254,40 @@ public final class DownloadManager {
} }
/** /**
* Handles the given {@link DownloadAction}. A task is created and added to the task queue. If * Handles the given action. A task is created and added to the task queue. If it's a remove
* it's a remove action then this method cancels any download tasks which works on the same media * action then any download tasks for the same media are immediately canceled.
* immediately.
* *
* @param downloadAction Action to be executed. * @param action The action to be executed.
* @return The task id. * @return The id of the newly created task.
*/ */
public int handleAction(DownloadAction downloadAction) { public int handleAction(DownloadAction action) {
DownloadTask downloadTask = createDownloadTask(downloadAction); Task task = createTask(action);
saveActions(); saveActions();
if (downloadsStopped && !downloadAction.isRemoveAction) { if (downloadsStopped && !action.isRemoveAction) {
logd("Can't start the task as downloads are stopped", downloadTask); logd("Can't start the task as downloads are stopped", task);
} else { } else {
maybeStartTasks(); maybeStartTasks();
} }
return downloadTask.id; return task.id;
} }
private DownloadTask createDownloadTask(DownloadAction downloadAction) { private Task createTask(DownloadAction action) {
DownloadTask downloadTask = new DownloadTask(nextTaskId++, this, downloadAction, minRetryCount); Task task = new Task(nextTaskId++, this, action, minRetryCount);
tasks.add(downloadTask); tasks.add(task);
logd("Task is added", downloadTask); logd("Task is added", task);
notifyListenersTaskStateChange(downloadTask); notifyListenersTaskStateChange(task);
return downloadTask; return task;
} }
/** Returns number of tasks. */ /** Returns the current number of tasks. */
public int getTaskCount() { public int getTaskCount() {
return tasks.size(); return tasks.size();
} }
/** Returns a {@link DownloadTask} for a task. */ /** Returns the state of a task, or null if no such task exists */
public DownloadState getDownloadState(int taskId) { public @Nullable TaskState getTaskState(int taskId) {
for (int i = 0; i < tasks.size(); i++) { for (int i = 0; i < tasks.size(); i++) {
DownloadTask task = tasks.get(i); Task task = tasks.get(i);
if (task.id == taskId) { if (task.id == taskId) {
return task.getDownloadState(); return task.getDownloadState();
} }
@ -296,14 +295,13 @@ public final class DownloadManager {
return null; return null;
} }
/** Returns {@link DownloadState}s for all tasks. */ /** Returns the states of all current tasks. */
public DownloadState[] getDownloadStates() { public TaskState[] getAllTaskStates() {
return getDownloadStates(tasks); TaskState[] states = new TaskState[tasks.size()];
for (int i = 0; i < states.length; i++) {
states[i] = tasks.get(i).getDownloadState();
} }
return states;
/** Returns an array of {@link DownloadState}s for active download tasks. */
public DownloadState[] getActiveDownloadStates() {
return getDownloadStates(activeDownloadTasks);
} }
/** Returns whether there are no active tasks. */ /** Returns whether there are no active tasks. */
@ -339,12 +337,12 @@ public final class DownloadManager {
boolean skipDownloadActions = downloadsStopped boolean skipDownloadActions = downloadsStopped
|| activeDownloadTasks.size() == maxActiveDownloadTasks; || activeDownloadTasks.size() == maxActiveDownloadTasks;
for (int i = 0; i < tasks.size(); i++) { for (int i = 0; i < tasks.size(); i++) {
DownloadTask downloadTask = tasks.get(i); Task task = tasks.get(i);
if (!downloadTask.canStart()) { if (!task.canStart()) {
continue; continue;
} }
DownloadAction downloadAction = downloadTask.downloadAction; DownloadAction downloadAction = task.action;
boolean removeAction = downloadAction.isRemoveAction; boolean removeAction = downloadAction.isRemoveAction;
if (!removeAction && skipDownloadActions) { if (!removeAction && skipDownloadActions) {
continue; continue;
@ -352,14 +350,14 @@ public final class DownloadManager {
boolean canStartTask = true; boolean canStartTask = true;
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
DownloadTask task = tasks.get(j); Task otherTask = tasks.get(j);
if (task.downloadAction.isSameMedia(downloadAction)) { if (otherTask.action.isSameMedia(downloadAction)) {
if (removeAction) { if (removeAction) {
canStartTask = false; canStartTask = false;
logd(downloadTask + " clashes with " + task); logd(task + " clashes with " + otherTask);
task.cancel(); otherTask.cancel();
// Continue loop to cancel any other preceding clashing tasks. // Continue loop to cancel any other preceding clashing tasks.
} else if (task.downloadAction.isRemoveAction) { } else if (otherTask.action.isRemoveAction) {
canStartTask = false; canStartTask = false;
skipDownloadActions = true; skipDownloadActions = true;
break; break;
@ -368,9 +366,9 @@ public final class DownloadManager {
} }
if (canStartTask) { if (canStartTask) {
downloadTask.start(); task.start();
if (!removeAction) { if (!removeAction) {
activeDownloadTasks.add(downloadTask); activeDownloadTasks.add(task);
skipDownloadActions = activeDownloadTasks.size() == maxActiveDownloadTasks; skipDownloadActions = activeDownloadTasks.size() == maxActiveDownloadTasks;
} }
} }
@ -387,18 +385,18 @@ public final class DownloadManager {
} }
} }
private void onTaskStateChange(DownloadTask downloadTask) { private void onTaskStateChange(Task task) {
if (released) { if (released) {
return; return;
} }
logd("Task state is changed", downloadTask); logd("Task state is changed", task);
boolean stopped = !downloadTask.isActive(); boolean stopped = !task.isActive();
if (stopped) { if (stopped) {
activeDownloadTasks.remove(downloadTask); activeDownloadTasks.remove(task);
} }
notifyListenersTaskStateChange(downloadTask); notifyListenersTaskStateChange(task);
if (downloadTask.isFinished()) { if (task.isFinished()) {
tasks.remove(downloadTask); tasks.remove(task);
saveActions(); saveActions();
} }
if (stopped) { if (stopped) {
@ -407,10 +405,10 @@ public final class DownloadManager {
} }
} }
private void notifyListenersTaskStateChange(DownloadTask downloadTask) { private void notifyListenersTaskStateChange(Task task) {
DownloadState downloadState = downloadTask.getDownloadState(); TaskState taskState = task.getDownloadState();
for (DownloadListener listener : listeners) { for (DownloadListener listener : listeners) {
listener.onStateChange(this, downloadState); listener.onTaskStateChanged(this, taskState);
} }
} }
@ -434,7 +432,7 @@ public final class DownloadManager {
public void run() { public void run() {
try { try {
for (DownloadAction action : actions) { for (DownloadAction action : actions) {
createDownloadTask(action); createTask(action);
} }
logd("Tasks are created."); logd("Tasks are created.");
maybeStartTasks(); maybeStartTasks();
@ -454,7 +452,7 @@ public final class DownloadManager {
} }
final DownloadAction[] actions = new DownloadAction[tasks.size()]; final DownloadAction[] actions = new DownloadAction[tasks.size()];
for (int i = 0; i < tasks.size(); i++) { for (int i = 0; i < tasks.size(); i++) {
actions[i] = tasks.get(i).downloadAction; actions[i] = tasks.get(i).action;
} }
fileIOHandler.post(new Runnable() { fileIOHandler.post(new Runnable() {
@Override @Override
@ -475,21 +473,12 @@ public final class DownloadManager {
} }
} }
private void logd(String message, DownloadTask task) { private void logd(String message, Task task) {
logd(message + ": " + task); logd(message + ": " + task);
} }
private static DownloadState[] getDownloadStates(ArrayList<DownloadTask> tasks) { /** Represents state of a task. */
DownloadState[] states = new DownloadState[tasks.size()]; public static final class TaskState {
for (int i = 0; i < tasks.size(); i++) {
DownloadTask task = tasks.get(i);
states[i] = task.getDownloadState();
}
return states;
}
/** Represents state of a download task. */
public static final class DownloadState {
/** /**
* Task states. * Task states.
@ -534,12 +523,13 @@ public final class DownloadManager {
} }
} }
/** Unique id of the task. */ /** The unique task id. */
public final int taskId; public final int taskId;
/** The {@link DownloadAction} which is being executed. */ /** The action being executed. */
public final DownloadAction downloadAction; public final DownloadAction action;
/** The state of the task. See {@link State}. */ /** The state of the task. */
public final @State int state; public final @State int state;
/** /**
* The estimated download percentage, or {@link C#PERCENTAGE_UNSET} if no estimate is available * The estimated download percentage, or {@link C#PERCENTAGE_UNSET} if no estimate is available
* or if this is a removal task. * or if this is a removal task.
@ -547,18 +537,19 @@ public final class DownloadManager {
public final float downloadPercentage; public final float downloadPercentage;
/** The total number of downloaded bytes. */ /** The total number of downloaded bytes. */
public final long downloadedBytes; public final long downloadedBytes;
/** If {@link #state} is {@link #STATE_ERROR} then this is the cause, otherwise null. */ /** If {@link #state} is {@link #STATE_ERROR} then this is the cause, otherwise null. */
public final Throwable error; public final Throwable error;
private DownloadState( private TaskState(
int taskId, int taskId,
DownloadAction downloadAction, DownloadAction action,
@State int state, @State int state,
float downloadPercentage, float downloadPercentage,
long downloadedBytes, long downloadedBytes,
Throwable error) { Throwable error) {
this.taskId = taskId; this.taskId = taskId;
this.downloadAction = downloadAction; this.action = action;
this.state = state; this.state = state;
this.downloadPercentage = downloadPercentage; this.downloadPercentage = downloadPercentage;
this.downloadedBytes = downloadedBytes; this.downloadedBytes = downloadedBytes;
@ -567,7 +558,7 @@ public final class DownloadManager {
} }
private static final class DownloadTask implements Runnable { private static final class Task implements Runnable {
/** /**
* Task states. * Task states.
@ -607,26 +598,26 @@ public final class DownloadManager {
private final int id; private final int id;
private final DownloadManager downloadManager; private final DownloadManager downloadManager;
private final DownloadAction downloadAction; private final DownloadAction action;
private final int minRetryCount; private final int minRetryCount;
private volatile @InternalState int currentState; private volatile @InternalState int currentState;
private volatile Downloader downloader; private volatile Downloader downloader;
private Thread thread; private Thread thread;
private Throwable error; private Throwable error;
private DownloadTask( private Task(
int id, DownloadManager downloadManager, DownloadAction downloadAction, int minRetryCount) { int id, DownloadManager downloadManager, DownloadAction action, int minRetryCount) {
this.id = id; this.id = id;
this.downloadManager = downloadManager; this.downloadManager = downloadManager;
this.downloadAction = downloadAction; this.action = action;
this.currentState = STATE_QUEUED; this.currentState = STATE_QUEUED;
this.minRetryCount = minRetryCount; this.minRetryCount = minRetryCount;
} }
public DownloadState getDownloadState() { public TaskState getDownloadState() {
int externalState = getExternalState(); int externalState = getExternalState();
return new DownloadState( return new TaskState(
id, downloadAction, externalState, getDownloadPercentage(), getDownloadedBytes(), error); id, action, externalState, getDownloadPercentage(), getDownloadedBytes(), error);
} }
/** Returns whether the task is finished. */ /** Returns whether the task is finished. */
@ -662,11 +653,11 @@ public final class DownloadManager {
if (!DEBUG) { if (!DEBUG) {
return super.toString(); return super.toString();
} }
return downloadAction.type return action.type
+ ' ' + ' '
+ (downloadAction.isRemoveAction ? "remove" : "download") + (action.isRemoveAction ? "remove" : "download")
+ ' ' + ' '
+ downloadAction.data + action.data
+ ' ' + ' '
+ getStateString(); + getStateString();
} }
@ -679,7 +670,7 @@ public final class DownloadManager {
case STATE_STARTED_STOPPING: case STATE_STARTED_STOPPING:
return "STOPPING"; return "STOPPING";
default: default:
return DownloadState.getStateString(currentState); return TaskState.getStateString(currentState);
} }
} }
@ -740,7 +731,7 @@ public final class DownloadManager {
this.error = error; this.error = error;
boolean isInternalState = currentState != getExternalState(); boolean isInternalState = currentState != getExternalState();
if (!isInternalState) { if (!isInternalState) {
downloadManager.onTaskStateChange(DownloadTask.this); downloadManager.onTaskStateChange(this);
} }
return true; return true;
} }
@ -749,11 +740,11 @@ public final class DownloadManager {
@Override @Override
public void run() { public void run() {
downloadManager.logd("Task is started", DownloadTask.this); downloadManager.logd("Task is started", this);
Throwable error = null; Throwable error = null;
try { try {
downloader = downloadAction.createDownloader(downloadManager.downloaderConstructorHelper); downloader = action.createDownloader(downloadManager.downloaderConstructorHelper);
if (downloadAction.isRemoveAction) { if (action.isRemoveAction) {
downloader.remove(); downloader.remove();
} else { } else {
int errorCount = 0; int errorCount = 0;

View File

@ -25,7 +25,7 @@ import android.os.Looper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.scheduler.RequirementsWatcher; import com.google.android.exoplayer2.scheduler.RequirementsWatcher;
import com.google.android.exoplayer2.scheduler.Scheduler; import com.google.android.exoplayer2.scheduler.Scheduler;
@ -272,21 +272,25 @@ public abstract class DownloadService extends Service {
/** /**
* Returns a notification to be displayed when this service running in the foreground. * Returns a notification to be displayed when this service running in the foreground.
* *
* <p>This method is called when there is a download task state change and periodically while * <p>This method is called when there is a task state change and periodically while there are
* there is an active download. Update interval can be set using {@link #DownloadService(int, * active tasks. The periodic update interval can be set using {@link #DownloadService(int,
* long)}. * long)}.
* *
* <p>On API level 26 and above, it may be also called just before the service stops with an empty * <p>On API level 26 and above, this method may also be called just before the service stops,
* {@code downloadStates} array, returned notification is used to satisfy system requirements for * with an empty {@code taskStates} array. The returned notification is used to satisfy system
* foreground services. * requirements for foreground services.
* *
* @param downloadStates DownloadState for all tasks. * @param taskStates The states of all current tasks.
* @return A notification to be displayed when this service running in the foreground. * @return The foreground notification to display.
*/ */
protected abstract Notification getForegroundNotification(DownloadState[] downloadStates); protected abstract Notification getForegroundNotification(TaskState[] taskStates);
/** Called when the download state changes. */ /**
protected void onStateChange(DownloadState downloadState) { * Called when the state of a task changes.
*
* @param taskState The state of the task.
*/
protected void onTaskStateChanged(TaskState taskState) {
// Do nothing. // Do nothing.
} }
@ -308,9 +312,9 @@ public abstract class DownloadService extends Service {
private final class DownloadListener implements DownloadManager.DownloadListener { private final class DownloadListener implements DownloadManager.DownloadListener {
@Override @Override
public void onStateChange(DownloadManager downloadManager, DownloadState downloadState) { public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) {
DownloadService.this.onStateChange(downloadState); DownloadService.this.onTaskStateChanged(taskState);
if (downloadState.state == DownloadState.STATE_STARTED) { if (taskState.state == TaskState.STATE_STARTED) {
foregroundNotificationUpdater.startPeriodicUpdates(); foregroundNotificationUpdater.startPeriodicUpdates();
} else { } else {
foregroundNotificationUpdater.update(); foregroundNotificationUpdater.update();
@ -349,8 +353,8 @@ public abstract class DownloadService extends Service {
} }
public void update() { public void update() {
DownloadState[] downloadStates = downloadManager.getDownloadStates(); TaskState[] taskStates = downloadManager.getAllTaskStates();
startForeground(notificationId, getForegroundNotification(downloadStates)); startForeground(notificationId, getForegroundNotification(taskStates));
notificationDisplayed = true; notificationDisplayed = true;
if (periodicUpdatesStarted) { if (periodicUpdatesStarted) {
handler.removeCallbacks(this); handler.removeCallbacks(this);

View File

@ -22,8 +22,8 @@ import android.os.ConditionVariable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener; import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State; import com.google.android.exoplayer2.offline.DownloadManager.TaskState.State;
import com.google.android.exoplayer2.testutil.DummyMainThread; import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.upstream.DummyDataSource;
@ -255,11 +255,11 @@ public class DownloadManagerTest {
downloadAction1.post().assertDoesNotStart(); downloadAction1.post().assertDoesNotStart();
downloadAction2.post().assertDoesNotStart(); downloadAction2.post().assertDoesNotStart();
DownloadState[] states = downloadManager.getDownloadStates(); TaskState[] states = downloadManager.getAllTaskStates();
assertThat(states).hasLength(3); assertThat(states).hasLength(3);
assertThat(states[0].downloadAction).isEqualTo(removeAction); assertThat(states[0].action).isEqualTo(removeAction);
assertThat(states[1].downloadAction).isEqualTo(downloadAction1); assertThat(states[1].action).isEqualTo(downloadAction1);
assertThat(states[2].downloadAction).isEqualTo(downloadAction2); assertThat(states[2].action).isEqualTo(downloadAction2);
} }
@Test @Test
@ -503,11 +503,11 @@ public class DownloadManagerTest {
} }
@Override @Override
public void onStateChange(DownloadManager downloadManager, DownloadState downloadState) { public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) {
if (downloadState.state == DownloadState.STATE_ERROR && downloadError == null) { if (taskState.state == TaskState.STATE_ERROR && downloadError == null) {
downloadError = downloadState.error; downloadError = taskState.error;
} }
((FakeDownloadAction) downloadState.downloadAction).onStateChange(downloadState.state); ((FakeDownloadAction) taskState.action).onStateChange(taskState.state);
} }
@Override @Override
@ -580,23 +580,23 @@ public class DownloadManagerTest {
private FakeDownloadAction assertStarted() throws InterruptedException { private FakeDownloadAction assertStarted() throws InterruptedException {
downloader.assertStarted(ASSERT_TRUE_TIMEOUT); downloader.assertStarted(ASSERT_TRUE_TIMEOUT);
return assertState(DownloadState.STATE_STARTED); return assertState(TaskState.STATE_STARTED);
} }
private FakeDownloadAction assertEnded() { private FakeDownloadAction assertEnded() {
return assertState(DownloadState.STATE_ENDED); return assertState(TaskState.STATE_ENDED);
} }
private FakeDownloadAction assertError() { private FakeDownloadAction assertError() {
return assertState(DownloadState.STATE_ERROR); return assertState(TaskState.STATE_ERROR);
} }
private FakeDownloadAction assertCancelled() { private FakeDownloadAction assertCancelled() {
return assertState(DownloadState.STATE_CANCELED); return assertState(TaskState.STATE_CANCELED);
} }
private FakeDownloadAction assertStopped() { private FakeDownloadAction assertStopped() {
return assertState(DownloadState.STATE_QUEUED); return assertState(TaskState.STATE_QUEUED);
} }
private FakeDownloadAction assertState(@State int expectedState) { private FakeDownloadAction assertState(@State int expectedState) {
@ -619,13 +619,13 @@ public class DownloadManagerTest {
if (i > 0) { if (i > 0) {
sb.append(','); sb.append(',');
} }
sb.append(DownloadState.getStateString(receivedStates.get(i))); sb.append(TaskState.getStateString(receivedStates.get(i)));
} }
fail( fail(
String.format( String.format(
Locale.US, Locale.US,
"expected:<%s> but was:<%s>", "expected:<%s> but was:<%s>",
DownloadState.getStateString(expectedState), TaskState.getStateString(expectedState),
sb)); sb));
} }
} }

View File

@ -25,7 +25,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper; import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.scheduler.Requirements;
@ -139,8 +139,7 @@ public class DownloadServiceDashTest {
} }
@Override @Override
protected Notification getForegroundNotification( protected Notification getForegroundNotification(TaskState[] taskStates) {
DownloadState[] downloadStates) {
return Mockito.mock(Notification.class); return Mockito.mock(Notification.class);
} }

View File

@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener; import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.testutil.DummyMainThread; import com.google.android.exoplayer2.testutil.DummyMainThread;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -40,9 +39,10 @@ import java.util.concurrent.TimeUnit;
} }
@Override @Override
public void onStateChange(DownloadManager downloadManager, DownloadState downloadState) { public void onTaskStateChanged(
if (downloadState.state == DownloadState.STATE_ERROR && downloadError == null) { DownloadManager downloadManager, DownloadManager.TaskState taskState) {
downloadError = downloadState.error; if (taskState.state == DownloadManager.TaskState.STATE_ERROR && downloadError == null) {
downloadError = taskState.error;
} }
} }

View File

@ -21,7 +21,7 @@ import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.util.ErrorMessageProvider; import com.google.android.exoplayer2.util.ErrorMessageProvider;
/** Helper class to create notifications for downloads using {@link DownloadManager}. */ /** Helper class to create notifications for downloads using {@link DownloadManager}. */
@ -32,18 +32,18 @@ public final class DownloadNotificationUtil {
private DownloadNotificationUtil() {} private DownloadNotificationUtil() {}
/** /**
* Returns a progress notification for the given {@link DownloadState}s. * Returns a progress notification for the given {@link TaskState}s.
* *
* @param downloadStates States of the downloads. * @param taskStates States of the downloads.
* @param context Used to access resources. * @param context Used to access resources.
* @param smallIcon A small icon for the notification. * @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 * @param channelId The id of the notification channel to use. Only required for API level 26 and
* above. * above.
* @param message An optional message to display on the notification. * @param message An optional message to display on the notification.
* @return A progress notification for the given {@link DownloadState}s. * @return A progress notification for the given {@link TaskState}s.
*/ */
public static @Nullable Notification createProgressNotification( public static @Nullable Notification createProgressNotification(
DownloadState[] downloadStates, TaskState[] taskStates,
Context context, Context context,
int smallIcon, int smallIcon,
String channelId, String channelId,
@ -52,16 +52,15 @@ public final class DownloadNotificationUtil {
int downloadTaskCount = 0; int downloadTaskCount = 0;
boolean allDownloadPercentagesUnknown = true; boolean allDownloadPercentagesUnknown = true;
boolean haveDownloadedBytes = false; boolean haveDownloadedBytes = false;
for (DownloadState downloadState : downloadStates) { for (TaskState taskState : taskStates) {
if (downloadState.downloadAction.isRemoveAction if (taskState.action.isRemoveAction || taskState.state != TaskState.STATE_STARTED) {
|| downloadState.state != DownloadState.STATE_STARTED) {
continue; continue;
} }
if (downloadState.downloadPercentage != C.PERCENTAGE_UNSET) { if (taskState.downloadPercentage != C.PERCENTAGE_UNSET) {
allDownloadPercentagesUnknown = false; allDownloadPercentagesUnknown = false;
totalPercentage += downloadState.downloadPercentage; totalPercentage += taskState.downloadPercentage;
} }
haveDownloadedBytes |= downloadState.downloadedBytes > 0; haveDownloadedBytes |= taskState.downloadedBytes > 0;
downloadTaskCount++; downloadTaskCount++;
} }
@ -79,11 +78,11 @@ public final class DownloadNotificationUtil {
} }
/** /**
* Returns a notification for a {@link DownloadState} which is in either {@link * Returns a notification for a {@link TaskState} which is in either {@link TaskState#STATE_ENDED}
* DownloadState#STATE_ENDED} or {@link DownloadState#STATE_ERROR} states. Returns null if it's * or {@link TaskState#STATE_ERROR} states. Returns null if it's some other state or it's state of
* some other state or it's state of a remove action. * a remove action.
* *
* @param downloadState State of the download. * @param taskState State of the download.
* @param context Used to access resources. * @param context Used to access resources.
* @param smallIcon A small icon for the notifications. * @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 * @param channelId The id of the notification channel to use. Only required for API level 26 and
@ -92,27 +91,26 @@ public final class DownloadNotificationUtil {
* @param errorMessageProvider An optional {@link ErrorMessageProvider} for translating download * @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 * errors into readable error messages. If not null and there is a download error then the
* error message is displayed instead of {@code message}. * error message is displayed instead of {@code message}.
* @return A notification for a {@link DownloadState} which is in either {@link * @return A notification for a {@link TaskState} which is in either {@link TaskState#STATE_ENDED}
* DownloadState#STATE_ENDED} or {@link DownloadState#STATE_ERROR} states. Returns null if * or {@link TaskState#STATE_ERROR} states. Returns null if it's some other state or it's
* it's some other state or it's state of a remove action. * state of a remove action.
*/ */
public static @Nullable Notification createDownloadFinishedNotification( public static @Nullable Notification createDownloadFinishedNotification(
DownloadState downloadState, TaskState taskState,
Context context, Context context,
int smallIcon, int smallIcon,
String channelId, String channelId,
@Nullable String message, @Nullable String message,
@Nullable ErrorMessageProvider<Throwable> errorMessageProvider) { @Nullable ErrorMessageProvider<Throwable> errorMessageProvider) {
if (downloadState.downloadAction.isRemoveAction if (taskState.action.isRemoveAction
|| (downloadState.state != DownloadState.STATE_ENDED || (taskState.state != TaskState.STATE_ENDED && taskState.state != TaskState.STATE_ERROR)) {
&& downloadState.state != DownloadState.STATE_ERROR)) {
return null; return null;
} }
if (downloadState.error != null && errorMessageProvider != null) { if (taskState.error != null && errorMessageProvider != null) {
message = errorMessageProvider.getErrorMessage(downloadState.error).second; message = errorMessageProvider.getErrorMessage(taskState.error).second;
} }
int titleStringId = int titleStringId =
downloadState.state == DownloadState.STATE_ENDED taskState.state == TaskState.STATE_ENDED
? R.string.exo_download_completed ? R.string.exo_download_completed
: R.string.exo_download_failed; : R.string.exo_download_failed;
NotificationCompat.Builder notificationBuilder = NotificationCompat.Builder notificationBuilder =