Refresh local feeds in DownloadService

This allows displaying the refresh state. Also, it is faster because
multiple local feeds can be refreshed in parallel.
This commit is contained in:
ByteHamster 2022-01-06 15:04:01 +01:00
parent 849bf4ad85
commit 7e27c8ce2e
6 changed files with 80 additions and 58 deletions

View File

@ -8,8 +8,9 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.danoeh.antennapod.model.feed.FeedFile;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.model.feed.FeedMedia;
public class DownloadRequest implements Parcelable {
public static final String REQUEST_ARG_PAGE_NR = "page";
@ -273,12 +274,20 @@ public class DownloadRequest implements Parcelable {
private Bundle arguments = new Bundle();
private boolean initiatedByUser = true;
public Builder(@NonNull String destination, @NonNull FeedFile item) {
public Builder(@NonNull String destination, @NonNull FeedMedia media) {
this.destination = destination;
this.source = URLChecker.prepareURL(item.getDownload_url());
this.title = item.getHumanReadableIdentifier();
this.feedfileId = item.getId();
this.feedfileType = item.getTypeAsInt();
this.source = URLChecker.prepareURL(media.getDownload_url());
this.title = media.getHumanReadableIdentifier();
this.feedfileId = media.getId();
this.feedfileType = media.getTypeAsInt();
}
public Builder(@NonNull String destination, @NonNull Feed feed) {
this.destination = destination;
this.source = feed.isLocalFeed() ? feed.getDownload_url() : URLChecker.prepareURL(feed.getDownload_url());
this.title = feed.getHumanReadableIdentifier();
this.feedfileId = feed.getId();
this.feedfileType = feed.getTypeAsInt();
}
public void setInitiatedByUser(boolean initiatedByUser) {

View File

@ -14,13 +14,13 @@ import java.io.File;
* Creates download requests that can be sent to the DownloadService.
*/
public class DownloadRequestCreator {
private static final String TAG = "DownloadRequester";
private static final String TAG = "DownloadRequestCreat";
private static final String FEED_DOWNLOADPATH = "cache/";
private static final String MEDIA_DOWNLOADPATH = "media/";
public static DownloadRequest.Builder create(Feed feed) {
File dest = new File(getFeedfilePath(), getFeedfileName(feed));
if (!isFilenameAvailable(dest.toString())) {
if (!isFilenameAvailable(dest.toString()) && !feed.isLocalFeed()) {
dest = findUnusedFile(dest);
}
Log.d(TAG, "Requesting download of url " + feed.getDownload_url());

View File

@ -20,6 +20,7 @@ import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import org.apache.commons.io.FileUtils;
import org.greenrobot.eventbus.EventBus;
@ -274,6 +275,10 @@ public class DownloadService extends Service {
DBTasks.autodownloadUndownloadedItems(getApplicationContext());
}
/**
* This method MUST NOT, in any case, throw an exception.
* Otherwise, it hangs up the refresh thread pool.
*/
private void performDownload(Downloader downloader) {
try {
downloader.call();
@ -295,6 +300,24 @@ public class DownloadService extends Service {
});
}
/**
* This method MUST NOT, in any case, throw an exception.
* Otherwise, it hangs up the refresh thread pool.
*/
private void performLocalFeedRefresh(Downloader downloader, DownloadRequest request) {
try {
Feed feed = DBReader.getFeed(request.getFeedfileId());
LocalFeedUpdater.updateFeed(feed, DownloadService.this);
} catch (Exception e) {
e.printStackTrace();
}
downloadEnqueueExecutor.submit(() -> {
downloads.remove(downloader);
stopServiceIfEverythingDone();
});
}
private void handleSuccessfulDownload(Downloader downloader) {
DownloadRequest request = downloader.getDownloadRequest();
DownloadStatus status = downloader.getResult();
@ -479,7 +502,7 @@ public class DownloadService extends Service {
boolean initiatedByUser = intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false);
List<Feed> feeds = DBReader.getFeedList();
for (Feed feed : feeds) {
if (feed.getPreferences().getKeepUpdated() && !feed.isLocalFeed()) {
if (feed.getPreferences().getKeepUpdated()) {
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
builder.setInitiatedByUser(initiatedByUser);
addNewRequest(builder.build());
@ -494,11 +517,18 @@ public class DownloadService extends Service {
Log.d(TAG, "Skipped enqueueing request. Already running.");
return;
}
writeFileUrl(request);
Downloader downloader = downloaderFactory.create(request);
if (downloader != null) {
Log.d(TAG, "Add new request: " + request.getSource());
if (request.getSource().startsWith(Feed.PREFIX_LOCAL_FOLDER)) {
Downloader downloader = new LocalFeedStubDownloader(request);
downloads.add(downloader);
downloadHandleExecutor.submit(() -> performDownload(downloader));
downloadHandleExecutor.submit(() -> performLocalFeedRefresh(downloader, request));
} else {
writeFileUrl(request);
Downloader downloader = downloaderFactory.create(request);
if (downloader != null) {
downloads.add(downloader);
downloadHandleExecutor.submit(() -> performDownload(downloader));
}
}
}

View File

@ -0,0 +1,17 @@
package de.danoeh.antennapod.core.service.download;
import androidx.annotation.NonNull;
/**
* This does not actually download, but it keeps track of a local feed's refresh state.
*/
public class LocalFeedStubDownloader extends Downloader {
public LocalFeedStubDownloader(@NonNull DownloadRequest request) {
super(request);
}
@Override
protected void download() {
}
}

View File

@ -31,7 +31,6 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.mapper.FeedCursorMapper;
@ -114,14 +113,6 @@ public final class DBTasks {
* @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
*/
public static void refreshAllFeeds(final Context context, boolean initiatedByUser) {
new Thread(() -> {
List<Feed> feeds = DBReader.getFeedList();
for (Feed feed : feeds) {
if (feed.isLocalFeed() && feed.getPreferences().getKeepUpdated()) {
LocalFeedUpdater.updateFeed(feed, context);
}
}
}).start();
DownloadService.refreshAllFeeds(context, initiatedByUser);
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
@ -169,15 +160,11 @@ public final class DBTasks {
}
private static void forceRefreshFeed(Context context, Feed feed, boolean loadAllPages, boolean initiatedByUser) {
if (feed.isLocalFeed()) {
new Thread(() -> LocalFeedUpdater.updateFeed(feed, context)).start();
} else {
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
builder.setInitiatedByUser(initiatedByUser);
builder.setForce(true);
builder.loadAllPages(loadAllPages);
DownloadService.download(context, false, builder.build());
}
DownloadRequest.Builder builder = DownloadRequestCreator.create(feed);
builder.setInitiatedByUser(initiatedByUser);
builder.setForce(true);
builder.loadAllPages(loadAllPages);
DownloadService.download(context, false, builder.build());
}
/**

View File

@ -3,14 +3,13 @@ package de.danoeh.antennapod.core.service.download;
import android.os.Bundle;
import android.os.Parcel;
import de.danoeh.antennapod.model.feed.FeedMedia;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import de.danoeh.antennapod.model.feed.FeedFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@ -40,7 +39,7 @@ public class DownloadRequestTest {
String destStr = "file://location/media.mp3";
String username = "testUser";
String password = "testPassword";
FeedFile item = createFeedItem(1);
FeedMedia item = createFeedItem(1);
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item)
.deleteOnFailure(true)
.withAuthentication(username, password)
@ -68,12 +67,12 @@ public class DownloadRequestTest {
ArrayList<DownloadRequest> toParcel;
{ // test DownloadRequests to parcel
String destStr = "file://location/media.mp3";
FeedFile item1 = createFeedItem(1);
FeedMedia item1 = createFeedItem(1);
DownloadRequest request1 = new DownloadRequest.Builder(destStr, item1)
.withAuthentication(username1, password1)
.build();
FeedFile item2 = createFeedItem(2);
FeedMedia item2 = createFeedItem(2);
DownloadRequest request2 = new DownloadRequest.Builder(destStr, item2)
.withAuthentication(username2, password2)
.build();
@ -118,28 +117,8 @@ public class DownloadRequestTest {
return sb.toString();
}
private FeedFile createFeedItem(final int id) {
private FeedMedia createFeedItem(final int id) {
// Use mockito would be less verbose, but it'll take extra 1 second for this tiny test
return new FeedFile() {
@Override
public long getId() {
return id;
}
@Override
public String getDownload_url() {
return "http://example.com/episode" + id;
}
@Override
public int getTypeAsInt() {
return 0;
}
@Override
public String getHumanReadableIdentifier() {
return "human-id-" + id;
}
};
return new FeedMedia(id, null, 0, 0, 0, "", "", "http://example.com/episode" + id, false, null, 0, 0);
}
}