Commit c04f5b9c by eguven Committed by Oliver Woodman

Add DownloadManager.removeDownload method

PiperOrigin-RevId: 240330799
parent 3919f384
...@@ -138,7 +138,7 @@ public final class DownloadManager { ...@@ -138,7 +138,7 @@ public final class DownloadManager {
private final HandlerThread fileIOThread; private final HandlerThread fileIOThread;
private final Handler fileIOHandler; private final Handler fileIOHandler;
private final CopyOnWriteArraySet<Listener> listeners; private final CopyOnWriteArraySet<Listener> listeners;
private final ArrayDeque<DownloadAction> actionQueue; private final ArrayDeque<DownloadUpdater> dowloadUpdateQueue;
private boolean initialized; private boolean initialized;
private boolean released; private boolean released;
...@@ -230,7 +230,7 @@ public final class DownloadManager { ...@@ -230,7 +230,7 @@ public final class DownloadManager {
fileIOHandler = new Handler(fileIOThread.getLooper()); fileIOHandler = new Handler(fileIOThread.getLooper());
listeners = new CopyOnWriteArraySet<>(); listeners = new CopyOnWriteArraySet<>();
actionQueue = new ArrayDeque<>(); dowloadUpdateQueue = new ArrayDeque<>();
setNotMetRequirements(watchRequirements(requirements)); setNotMetRequirements(watchRequirements(requirements));
loadDownloads(); loadDownloads();
...@@ -348,9 +348,56 @@ public final class DownloadManager { ...@@ -348,9 +348,56 @@ public final class DownloadManager {
*/ */
public void handleAction(DownloadAction action) { public void handleAction(DownloadAction action) {
Assertions.checkState(!released); Assertions.checkState(!released);
actionQueue.add(action); dowloadUpdateQueue.add(
new DownloadUpdater(action.id) {
@Override
void onExisting(Download download) {
download.addAction(action);
logd("Action is added to existing download", download);
}
@Override
DownloadState onLoad(@Nullable DownloadState downloadState) {
DownloadState state;
if (downloadState == null) {
state = new DownloadState(action);
logd("Download state is created for " + id);
} else {
state = downloadState.mergeAction(action);
logd("Download state is loaded for " + id);
}
return state;
}
});
if (initialized) { if (initialized) {
processActionQueue(); processDownloadUpdateQueue();
}
}
/**
* Cancels the download with the {@code id} and removes all downloaded data.
*
* @param id The unique content id of the download to be started.
*/
public void removeDownload(String id) {
Assertions.checkState(!released);
dowloadUpdateQueue.add(
new DownloadUpdater(id) {
@Override
void onExisting(Download download) {
download.remove();
}
@Override
DownloadState onLoad(@Nullable DownloadState downloadState) {
if (downloadState != null) {
downloadState = downloadState.setRemoveState();
}
return downloadState;
}
});
if (initialized) {
processDownloadUpdateQueue();
} }
} }
...@@ -379,7 +426,10 @@ public final class DownloadManager { ...@@ -379,7 +426,10 @@ public final class DownloadManager {
/** Returns whether there are no active downloads. */ /** Returns whether there are no active downloads. */
public boolean isIdle() { public boolean isIdle() {
Assertions.checkState(!released); Assertions.checkState(!released);
return initialized && activeDownloads.isEmpty() && actionQueue.isEmpty() && !loadingDownload; return initialized
&& activeDownloads.isEmpty()
&& dowloadUpdateQueue.isEmpty()
&& !loadingDownload;
} }
/** /**
...@@ -490,27 +540,26 @@ public final class DownloadManager { ...@@ -490,27 +540,26 @@ public final class DownloadManager {
return null; return null;
} }
private void processActionQueue() { private void processDownloadUpdateQueue() {
if (loadingDownload || actionQueue.isEmpty()) { if (loadingDownload || dowloadUpdateQueue.isEmpty()) {
return; return;
} }
DownloadAction action = actionQueue.remove(); DownloadUpdater downloadUpdater = dowloadUpdateQueue.remove();
Download download = getDownload(action.id); Download download = getDownload(downloadUpdater.id);
if (download != null) { if (download != null) {
download.addAction(action); downloadUpdater.onExisting(download);
logd("Action is added to existing download", download); } else {
return; loadDownload(downloadUpdater);
} }
loadDownload(action);
} }
private void loadDownload(DownloadAction action) { private void loadDownload(DownloadUpdater callback) {
loadingDownload = true; loadingDownload = true;
fileIOHandler.post( fileIOHandler.post(
() -> { () -> {
DownloadState downloadState = null; DownloadState downloadState = null;
try { try {
downloadState = downloadIndex.getDownloadState(action.id); downloadState = downloadIndex.getDownloadState(callback.id);
} catch (DatabaseIOException e) { } catch (DatabaseIOException e) {
Log.e(TAG, "loadDownload failed", e); Log.e(TAG, "loadDownload failed", e);
} }
...@@ -518,19 +567,13 @@ public final class DownloadManager { ...@@ -518,19 +567,13 @@ public final class DownloadManager {
handler.post( handler.post(
() -> { () -> {
loadingDownload = false; loadingDownload = false;
if (released) { if (!released) {
return; DownloadState state = callback.onLoad(finalDownloadState);
if (state != null) {
addDownloadForState(state);
} }
DownloadState state; processDownloadUpdateQueue();
if (finalDownloadState == null) {
state = new DownloadState(action);
logd("Download state is created for " + action.id);
} else {
state = finalDownloadState.mergeAction(action);
logd("Download state is loaded for " + action.id);
} }
addDownloadForState(state);
processActionQueue();
}); });
}); });
} }
...@@ -568,7 +611,7 @@ public final class DownloadManager { ...@@ -568,7 +611,7 @@ public final class DownloadManager {
} }
logd("Downloads are created."); logd("Downloads are created.");
initialized = true; initialized = true;
processActionQueue(); processDownloadUpdateQueue();
for (Listener listener : listeners) { for (Listener listener : listeners) {
listener.onInitialized(DownloadManager.this); listener.onInitialized(DownloadManager.this);
} }
...@@ -730,6 +773,10 @@ public final class DownloadManager { ...@@ -730,6 +773,10 @@ public final class DownloadManager {
initialize(downloadState.state); initialize(downloadState.state);
} }
public void remove() {
initialize(STATE_REMOVING);
}
public DownloadState getUpdatedDownloadState() { public DownloadState getUpdatedDownloadState() {
float downloadPercentage = C.PERCENTAGE_UNSET; float downloadPercentage = C.PERCENTAGE_UNSET;
long downloadedBytes = 0; long downloadedBytes = 0;
...@@ -948,4 +995,15 @@ public final class DownloadManager { ...@@ -948,4 +995,15 @@ public final class DownloadManager {
} }
} }
private abstract static class DownloadUpdater {
final String id;
private DownloadUpdater(String id) {
this.id = id;
}
abstract void onExisting(Download download);
abstract DownloadState onLoad(@Nullable DownloadState downloadState);
}
} }
...@@ -51,6 +51,10 @@ public abstract class DownloadService extends Service { ...@@ -51,6 +51,10 @@ public abstract class DownloadService extends Service {
public static final String ACTION_START = public static final String ACTION_START =
"com.google.android.exoplayer.downloadService.action.START"; "com.google.android.exoplayer.downloadService.action.START";
/** Removes one download. */
public static final String ACTION_REMOVE =
"com.google.android.exoplayer.downloadService.action.REMOVE";
/** Like {@link #ACTION_INIT}, but with {@link #KEY_FOREGROUND} implicitly set to true. */ /** Like {@link #ACTION_INIT}, but with {@link #KEY_FOREGROUND} implicitly set to true. */
private static final String ACTION_RESTART = private static final String ACTION_RESTART =
"com.google.android.exoplayer.downloadService.action.RESTART"; "com.google.android.exoplayer.downloadService.action.RESTART";
...@@ -58,7 +62,10 @@ public abstract class DownloadService extends Service { ...@@ -58,7 +62,10 @@ public abstract class DownloadService extends Service {
/** Key for the {@link DownloadAction} in an {@link #ACTION_ADD} intent. */ /** Key for the {@link DownloadAction} in an {@link #ACTION_ADD} intent. */
public static final String KEY_DOWNLOAD_ACTION = "download_action"; public static final String KEY_DOWNLOAD_ACTION = "download_action";
/** Key for content id in an {@link #ACTION_STOP} or {@link #ACTION_START} intent. */ /**
* Key for content id in an {@link #ACTION_STOP}, {@link #ACTION_START} or {@link #ACTION_REMOVE}
* intent.
*/
public static final String KEY_CONTENT_ID = "content_id"; public static final String KEY_CONTENT_ID = "content_id";
/** Key for manual stop reason in an {@link #ACTION_STOP} intent. */ /** Key for manual stop reason in an {@link #ACTION_STOP} intent. */
...@@ -214,6 +221,19 @@ public abstract class DownloadService extends Service { ...@@ -214,6 +221,19 @@ public abstract class DownloadService extends Service {
} }
/** /**
* Builds an {@link Intent} for removing the download with the {@code id}.
*
* @param context A {@link Context}.
* @param clazz The concrete download service being targeted by the intent.
* @param id The content id.
* @return Created Intent.
*/
public static Intent buildRemoveDownloadIntent(
Context context, Class<? extends DownloadService> clazz, String id) {
return getIntent(context, clazz, ACTION_REMOVE).putExtra(KEY_CONTENT_ID, id);
}
/**
* Starts the service, adding an action to be executed. * Starts the service, adding an action to be executed.
* *
* @param context A {@link Context}. * @param context A {@link Context}.
...@@ -235,6 +255,24 @@ public abstract class DownloadService extends Service { ...@@ -235,6 +255,24 @@ public abstract class DownloadService extends Service {
} }
/** /**
* Starts the service to remove a download.
*
* @param context A {@link Context}.
* @param clazz The concrete download service to be started.
* @param id The content id.
* @param foreground Whether the service is started in the foreground.
*/
public static void startWithRemoveDownload(
Context context, Class<? extends DownloadService> clazz, String id, boolean foreground) {
Intent intent = buildRemoveDownloadIntent(context, clazz, id);
if (foreground) {
Util.startForegroundService(context, intent);
} else {
context.startService(intent);
}
}
/**
* Starts the service without adding a new action. If there are any not finished actions and the * Starts the service without adding a new action. If there are any not finished actions and the
* requirements are met, the service resumes executing actions. Otherwise it stops immediately. * requirements are met, the service resumes executing actions. Otherwise it stops immediately.
* *
...@@ -328,6 +366,14 @@ public abstract class DownloadService extends Service { ...@@ -328,6 +366,14 @@ public abstract class DownloadService extends Service {
downloadManager.startDownload(startDownloadId); downloadManager.startDownload(startDownloadId);
} }
break; break;
case ACTION_REMOVE:
String id3 = intent.getStringExtra(KEY_CONTENT_ID);
if (id3 == null) {
Log.e(TAG, "Ignoring REMOVE action with no id");
} else {
downloadManager.removeDownload(id3);
}
break;
default: default:
Log.e(TAG, "Ignoring unrecognized action: " + intentAction); Log.e(TAG, "Ignoring unrecognized action: " + intentAction);
break; break;
......
...@@ -245,6 +245,26 @@ public final class DownloadState { ...@@ -245,6 +245,26 @@ public final class DownloadState {
action.data); action.data);
} }
/** Returns a duplicate {@link DownloadState} in {@link #STATE_REMOVING}. */
public DownloadState setRemoveState() {
return new DownloadState(
id,
type,
uri,
cacheKey,
getNextState(state, manualStopReason != 0 || notMetRequirements != 0, true),
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
downloadedBytes,
/* totalBytes= */ C.LENGTH_UNSET,
FAILURE_REASON_NONE,
notMetRequirements,
manualStopReason,
startTimeMs,
/* updateTimeMs= */ System.currentTimeMillis(),
streamKeys,
customMetadata);
}
private static int getNextState(int currentState, boolean isStopped, boolean remove) { private static int getNextState(int currentState, boolean isStopped, boolean remove) {
int nextState; int nextState;
if (remove) { if (remove) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment