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 {
private final HandlerThread fileIOThread;
private final Handler fileIOHandler;
private final CopyOnWriteArraySet<Listener> listeners;
private final ArrayDeque<DownloadAction> actionQueue;
private final ArrayDeque<DownloadUpdater> dowloadUpdateQueue;
private boolean initialized;
private boolean released;
......@@ -230,7 +230,7 @@ public final class DownloadManager {
fileIOHandler = new Handler(fileIOThread.getLooper());
listeners = new CopyOnWriteArraySet<>();
actionQueue = new ArrayDeque<>();
dowloadUpdateQueue = new ArrayDeque<>();
setNotMetRequirements(watchRequirements(requirements));
loadDownloads();
......@@ -348,9 +348,56 @@ public final class DownloadManager {
*/
public void handleAction(DownloadAction action) {
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) {
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) {
processActionQueue();
processDownloadUpdateQueue();
}
}
......@@ -379,7 +426,10 @@ public final class DownloadManager {
/** Returns whether there are no active downloads. */
public boolean isIdle() {
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 {
return null;
}
private void processActionQueue() {
if (loadingDownload || actionQueue.isEmpty()) {
private void processDownloadUpdateQueue() {
if (loadingDownload || dowloadUpdateQueue.isEmpty()) {
return;
}
DownloadAction action = actionQueue.remove();
Download download = getDownload(action.id);
DownloadUpdater downloadUpdater = dowloadUpdateQueue.remove();
Download download = getDownload(downloadUpdater.id);
if (download != null) {
download.addAction(action);
logd("Action is added to existing download", download);
return;
downloadUpdater.onExisting(download);
} else {
loadDownload(downloadUpdater);
}
loadDownload(action);
}
private void loadDownload(DownloadAction action) {
private void loadDownload(DownloadUpdater callback) {
loadingDownload = true;
fileIOHandler.post(
() -> {
DownloadState downloadState = null;
try {
downloadState = downloadIndex.getDownloadState(action.id);
downloadState = downloadIndex.getDownloadState(callback.id);
} catch (DatabaseIOException e) {
Log.e(TAG, "loadDownload failed", e);
}
......@@ -518,19 +567,13 @@ public final class DownloadManager {
handler.post(
() -> {
loadingDownload = false;
if (released) {
return;
if (!released) {
DownloadState state = callback.onLoad(finalDownloadState);
if (state != null) {
addDownloadForState(state);
}
processDownloadUpdateQueue();
}
DownloadState state;
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 {
}
logd("Downloads are created.");
initialized = true;
processActionQueue();
processDownloadUpdateQueue();
for (Listener listener : listeners) {
listener.onInitialized(DownloadManager.this);
}
......@@ -730,6 +773,10 @@ public final class DownloadManager {
initialize(downloadState.state);
}
public void remove() {
initialize(STATE_REMOVING);
}
public DownloadState getUpdatedDownloadState() {
float downloadPercentage = C.PERCENTAGE_UNSET;
long downloadedBytes = 0;
......@@ -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 {
public static final String 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. */
private static final String ACTION_RESTART =
"com.google.android.exoplayer.downloadService.action.RESTART";
......@@ -58,7 +62,10 @@ 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. */
/**
* 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";
/** Key for manual stop reason in an {@link #ACTION_STOP} intent. */
......@@ -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.
*
* @param context A {@link Context}.
......@@ -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
* requirements are met, the service resumes executing actions. Otherwise it stops immediately.
*
......@@ -328,6 +366,14 @@ public abstract class DownloadService extends Service {
downloadManager.startDownload(startDownloadId);
}
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:
Log.e(TAG, "Ignoring unrecognized action: " + intentAction);
break;
......
......@@ -245,6 +245,26 @@ public final class DownloadState {
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) {
int nextState;
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