summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-11-10 21:01:41 +0100
committerdaniel oeh <daniel.oeh@gmail.com>2014-11-10 21:01:41 +0100
commitad04a80ae7b2091c94b0d007c2a78d97e7af871d (patch)
tree0d43b619ddcc6ea6d1222595d7a56db5a8c6e823 /core
parent6035e8fee91d895dd3a0fcedbedab2bc9de5b6a1 (diff)
downloadAntennaPod-ad04a80ae7b2091c94b0d007c2a78d97e7af871d.zip
Squashed commit of the following:
commit 7d0e6d5c2dc75f8f1e54f4a1279c6b0cffa7b8cc Author: daniel oeh <daniel.oeh@gmail.com> Date: Mon Nov 10 21:00:58 2014 +0100 Made tests compile commit 05c57df87dd469d8f64835700eefe2e3c87e04e4 Author: daniel oeh <daniel.oeh@gmail.com> Date: Mon Nov 10 20:56:58 2014 +0100 Removed unused code commit 0a1fbc9e6d9648646140e30dec0ec8389fb8d37f Author: daniel oeh <daniel.oeh@gmail.com> Date: Thu Nov 6 15:48:24 2014 +0100 Added UI controls to download more feed pages closes #245 commit 6486fb40f1d03887e264df95946f91f0a9cdac9b Author: daniel oeh <daniel.oeh@gmail.com> Date: Thu Nov 6 14:49:43 2014 +0100 Added support for downloading feed pages commit e1faa06908bfd50f2aa0c28ee5118772c4281557 Author: daniel oeh <daniel.oeh@gmail.com> Date: Wed Nov 5 21:27:03 2014 +0100 Added "paged" and "loadAllPages" attributes
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java85
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java78
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java100
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java52
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java56
-rw-r--r--core/src/main/res/layout/more_content_list_footer.xml26
-rw-r--r--core/src/main/res/values/strings.xml2
12 files changed, 382 insertions, 80 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index 3f83ab8b6..c0f71ed55 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -3,6 +3,12 @@ package de.danoeh.antennapod.core.feed;
import android.content.Context;
import android.net.Uri;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -10,10 +16,6 @@ import de.danoeh.antennapod.core.util.EpisodeFilter;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
/**
* Data Object for a whole feed
*
@@ -59,11 +61,32 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
private FeedPreferences preferences;
/**
+ * The page number that this feed is on. Only feeds with page number "0" should be stored in the
+ * database, feed objects with a higher page number only exist temporarily and should be merged
+ * into feeds with page number "0".
+ * <p/>
+ * This attribute's value is not saved in the database
+ */
+ private int pageNr;
+
+ /**
+ * True if this is a "paged feed", i.e. there exist other feed files that belong to the same
+ * logical feed.
+ */
+ private boolean paged;
+
+ /**
+ * Link to the next page of this feed. If this feed object represents a logical feed (i.e. a feed
+ * that is saved in the database) this might be null while still being a paged feed.
+ */
+ private String nextPageLink;
+
+ /**
* This constructor is used for restoring a feed from the database.
*/
public Feed(long id, Date lastUpdate, String title, String link, String description, String paymentLink,
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
- String downloadUrl, boolean downloaded, FlattrStatus status) {
+ String downloadUrl, boolean downloaded, FlattrStatus status, boolean paged, String nextPageLink) {
super(fileUrl, downloadUrl, downloaded);
this.id = id;
this.title = title;
@@ -81,6 +104,8 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
this.feedIdentifier = feedIdentifier;
this.image = image;
this.flattrStatus = status;
+ this.paged = paged;
+ this.nextPageLink = nextPageLink;
items = new ArrayList<FeedItem>();
}
@@ -92,7 +117,7 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
String downloadUrl, boolean downloaded) {
this(id, lastUpdate, title, link, description, paymentLink, author, language, type, feedIdentifier, image,
- fileUrl, downloadUrl, downloaded, new FlattrStatus());
+ fileUrl, downloadUrl, downloaded, new FlattrStatus(), false, null);
}
/**
@@ -270,6 +295,12 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
if (other.flattrStatus != null) {
flattrStatus = other.flattrStatus;
}
+ // this feed's nextPage might already point to a higher page, so we only update the nextPage value
+ // if this feed is not paged and the other feed is.
+ if (!this.paged && other.paged) {
+ this.paged = other.paged;
+ this.nextPageLink = other.nextPageLink;
+ }
}
public boolean compareWithOther(Feed other) {
@@ -310,6 +341,12 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
return true;
}
}
+ if (other.isPaged() && !this.isPaged()) {
+ return true;
+ }
+ if (!StringUtils.equals(other.getNextPageLink(), this.getNextPageLink())) {
+ return true;
+ }
return false;
}
@@ -374,13 +411,13 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
this.feedIdentifier = feedIdentifier;
}
- public void setFlattrStatus(FlattrStatus status) {
- this.flattrStatus = status;
- }
+ public void setFlattrStatus(FlattrStatus status) {
+ this.flattrStatus = status;
+ }
- public FlattrStatus getFlattrStatus() {
- return flattrStatus;
- }
+ public FlattrStatus getFlattrStatus() {
+ return flattrStatus;
+ }
public String getPaymentLink() {
return paymentLink;
@@ -442,4 +479,28 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
return null;
}
}
+
+ public int getPageNr() {
+ return pageNr;
+ }
+
+ public void setPageNr(int pageNr) {
+ this.pageNr = pageNr;
+ }
+
+ public boolean isPaged() {
+ return paged;
+ }
+
+ public void setPaged(boolean paged) {
+ this.paged = paged;
+ }
+
+ public String getNextPageLink() {
+ return nextPageLink;
+ }
+
+ public void setNextPageLink(String nextPageLink) {
+ this.nextPageLink = nextPageLink;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
index c79da0a48..75d6570b2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.service.download;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -15,6 +16,7 @@ public class DownloadRequest implements Parcelable {
private boolean deleteOnFailure;
private final long feedfileId;
private final int feedfileType;
+ private final Bundle arguments;
protected int progressPercent;
protected long soFar;
@@ -22,7 +24,7 @@ public class DownloadRequest implements Parcelable {
protected int statusMsg;
public DownloadRequest(String destination, String source, String title,
- long feedfileId, int feedfileType, String username, String password, boolean deleteOnFailure) {
+ long feedfileId, int feedfileType, String username, String password, boolean deleteOnFailure, Bundle arguments) {
Validate.notNull(destination);
Validate.notNull(source);
Validate.notNull(title);
@@ -35,11 +37,12 @@ public class DownloadRequest implements Parcelable {
this.username = username;
this.password = password;
this.deleteOnFailure = deleteOnFailure;
+ this.arguments = (arguments != null) ? arguments : new Bundle();
}
public DownloadRequest(String destination, String source, String title,
long feedfileId, int feedfileType) {
- this(destination, source, title, feedfileId, feedfileType, null, null, true);
+ this(destination, source, title, feedfileId, feedfileType, null, null, true, null);
}
private DownloadRequest(Parcel in) {
@@ -49,6 +52,7 @@ public class DownloadRequest implements Parcelable {
feedfileId = in.readLong();
feedfileType = in.readInt();
deleteOnFailure = (in.readByte() > 0);
+ arguments = in.readBundle();
if (in.dataAvail() > 0) {
username = in.readString();
} else {
@@ -74,6 +78,7 @@ public class DownloadRequest implements Parcelable {
dest.writeLong(feedfileId);
dest.writeInt(feedfileType);
dest.writeByte((deleteOnFailure) ? (byte) 1 : 0);
+ dest.writeBundle(arguments);
if (username != null) {
dest.writeString(username);
}
@@ -92,6 +97,7 @@ public class DownloadRequest implements Parcelable {
}
};
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -106,11 +112,11 @@ public class DownloadRequest implements Parcelable {
if (size != that.size) return false;
if (soFar != that.soFar) return false;
if (statusMsg != that.statusMsg) return false;
- if (destination != null ? !destination.equals(that.destination) : that.destination != null)
- return false;
+ if (!arguments.equals(that.arguments)) return false;
+ if (!destination.equals(that.destination)) return false;
if (password != null ? !password.equals(that.password) : that.password != null)
return false;
- if (source != null ? !source.equals(that.source) : that.source != null) return false;
+ if (!source.equals(that.source)) return false;
if (title != null ? !title.equals(that.title) : that.title != null) return false;
if (username != null ? !username.equals(that.username) : that.username != null)
return false;
@@ -120,14 +126,15 @@ public class DownloadRequest implements Parcelable {
@Override
public int hashCode() {
- int result = destination != null ? destination.hashCode() : 0;
- result = 31 * result + (source != null ? source.hashCode() : 0);
+ int result = destination.hashCode();
+ result = 31 * result + source.hashCode();
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (deleteOnFailure ? 1 : 0);
result = 31 * result + (int) (feedfileId ^ (feedfileId >>> 32));
result = 31 * result + feedfileType;
+ result = 31 * result + arguments.hashCode();
result = 31 * result + progressPercent;
result = 31 * result + (int) (soFar ^ (soFar >>> 32));
result = 31 * result + (int) (size ^ (size >>> 32));
@@ -206,4 +213,8 @@ public class DownloadRequest implements Parcelable {
public boolean isDeleteOnFailure() {
return deleteOnFailure;
}
+
+ public Bundle getArguments() {
+ return arguments;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index b8db5a387..02a6aecbd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -15,6 +15,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.util.Pair;
import android.util.Log;
import android.webkit.URLUtil;
@@ -66,6 +67,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DownloadError;
@@ -669,7 +671,7 @@ public class DownloadService extends Service {
private static final String TAG = "FeedSyncThread";
private BlockingQueue<DownloadRequest> completedRequests = new LinkedBlockingDeque<DownloadRequest>();
- private CompletionService<Feed> parserService = new ExecutorCompletionService<Feed>(Executors.newSingleThreadExecutor());
+ private CompletionService<Pair<DownloadRequest, FeedHandlerResult>> parserService = new ExecutorCompletionService<Pair<DownloadRequest, FeedHandlerResult>>(Executors.newSingleThreadExecutor());
private ExecutorService dbService = Executors.newSingleThreadExecutor();
private Future<?> dbUpdateFuture;
private volatile boolean isActive = true;
@@ -684,8 +686,8 @@ public class DownloadService extends Service {
*
* @return Collected feeds or null if the method has been interrupted during the first waiting period.
*/
- private List<Feed> collectCompletedRequests() {
- List<Feed> results = new LinkedList<Feed>();
+ private List<Pair<DownloadRequest, FeedHandlerResult>> collectCompletedRequests() {
+ List<Pair<DownloadRequest, FeedHandlerResult>> results = new LinkedList<Pair<DownloadRequest, FeedHandlerResult>>();
DownloadRequester requester = DownloadRequester.getInstance();
int tasks = 0;
@@ -727,9 +729,9 @@ public class DownloadService extends Service {
for (int i = 0; i < tasks; i++) {
try {
- Feed f = parserService.take().get();
- if (f != null) {
- results.add(f);
+ Pair<DownloadRequest, FeedHandlerResult> result = parserService.take().get();
+ if (result != null) {
+ results.add(result);
}
} catch (InterruptedException e) {
e.printStackTrace();
@@ -754,16 +756,16 @@ public class DownloadService extends Service {
@Override
public void run() {
while (isActive) {
- final List<Feed> feeds = collectCompletedRequests();
+ final List<Pair<DownloadRequest, FeedHandlerResult>> results = collectCompletedRequests();
- if (feeds == null) {
+ if (results == null) {
continue;
}
- if (BuildConfig.DEBUG) Log.d(TAG, "Bundling " + feeds.size() + " feeds");
+ if (BuildConfig.DEBUG) Log.d(TAG, "Bundling " + results.size() + " feeds");
- for (Feed feed : feeds) {
- removeDuplicateImages(feed); // duplicate images have to removed because the DownloadRequester does not accept two downloads with the same download URL yet.
+ for (Pair<DownloadRequest, FeedHandlerResult> result : results) {
+ removeDuplicateImages(result.second.feed); // duplicate images have to removed because the DownloadRequester does not accept two downloads with the same download URL yet.
}
// Save information of feed in DB
@@ -780,9 +782,10 @@ public class DownloadService extends Service {
dbUpdateFuture = dbService.submit(new Runnable() {
@Override
public void run() {
- Feed[] savedFeeds = DBTasks.updateFeed(DownloadService.this, feeds.toArray(new Feed[feeds.size()]));
+ Feed[] savedFeeds = DBTasks.updateFeed(DownloadService.this, getFeeds(results));
- for (Feed savedFeed : savedFeeds) {
+ for (int i = 0; i < savedFeeds.length; i++) {
+ Feed savedFeed = savedFeeds[i];
// Download Feed Image if provided and not downloaded
if (savedFeed.getImage() != null
&& savedFeed.getImage().isDownloaded() == false) {
@@ -816,6 +819,19 @@ public class DownloadService extends Service {
}
}
+ // If loadAllPages=true, check if another page is available and queue it for download
+
+ final boolean loadAllPages = results.get(i).first.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
+ final Feed feed = results.get(i).second.feed;
+ if (loadAllPages && feed.getNextPageLink() != null) {
+ try {
+ feed.setId(savedFeed.getId());
+ DBTasks.loadNextPageOfFeed(DownloadService.this, savedFeed, true);
+ } catch (DownloadRequestException e) {
+ Log.e(TAG, "Error trying to load next page", e);
+ }
+ }
+
ClientConfig.downloadServiceCallbacks.onFeedParsed(DownloadService.this,
savedFeed);
@@ -840,12 +856,22 @@ public class DownloadService extends Service {
}
-
if (BuildConfig.DEBUG) Log.d(TAG, "Shutting down");
}
- private class FeedParserTask implements Callable<Feed> {
+ /**
+ * Helper method
+ */
+ private Feed[] getFeeds(List<Pair<DownloadRequest, FeedHandlerResult>> results) {
+ Feed[] feeds = new Feed[results.size()];
+ for (int i = 0; i < results.size(); i++) {
+ feeds[i] = results.get(i).second.feed;
+ }
+ return feeds;
+ }
+
+ private class FeedParserTask implements Callable<Pair<DownloadRequest, FeedHandlerResult>> {
private DownloadRequest request;
@@ -854,27 +880,28 @@ public class DownloadService extends Service {
}
@Override
- public Feed call() throws Exception {
+ public Pair<DownloadRequest, FeedHandlerResult> call() throws Exception {
return parseFeed(request);
}
}
- private Feed parseFeed(DownloadRequest request) {
- Feed savedFeed = null;
-
+ private Pair<DownloadRequest, FeedHandlerResult> parseFeed(DownloadRequest request) {
Feed feed = new Feed(request.getSource(), new Date());
feed.setFile_url(request.getDestination());
feed.setId(request.getFeedfileId());
feed.setDownloaded(true);
- feed.setPreferences(new FeedPreferences(0, true, request.getUsername(), request.getPassword()));
+ feed.setPreferences(new FeedPreferences(0, true,
+ request.getUsername(), request.getPassword()));
+ feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
DownloadError reason = null;
String reasonDetailed = null;
boolean successful = true;
FeedHandler feedHandler = new FeedHandler();
+ FeedHandlerResult result = null;
try {
- feed = feedHandler.parseFeed(feed).feed;
+ result = feedHandler.parseFeed(feed);
if (BuildConfig.DEBUG)
Log.d(TAG, feed.getTitle() + " parsed");
if (checkFeedData(feed) == false) {
@@ -909,17 +936,14 @@ public class DownloadService extends Service {
}
// cleanup();
- if (savedFeed == null) {
- savedFeed = feed;
- }
if (successful) {
- return savedFeed;
+ return Pair.create(request, result);
} else {
numberOfDownloads.decrementAndGet();
- saveDownloadStatus(new DownloadStatus(savedFeed,
- savedFeed.getHumanReadableIdentifier(), reason, successful,
+ saveDownloadStatus(new DownloadStatus(feed,
+ feed.getHumanReadableIdentifier(), reason, successful,
reasonDetailed));
return null;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index 9cc87cb47..6c0b6df74 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -350,7 +350,9 @@ public final class DBReader {
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_FILE_URL),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_DOWNLOAD_URL),
cursor.getInt(PodDBAdapter.IDX_FEED_SEL_STD_DOWNLOADED) > 0,
- new FlattrStatus(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_FLATTR_STATUS)));
+ new FlattrStatus(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_FLATTR_STATUS)),
+ cursor.getInt(PodDBAdapter.IDX_FEED_SEL_STD_IS_PAGED) > 0,
+ cursor.getString(PodDBAdapter.IDX_FEED_SEL_STD_NEXT_PAGE_LINK));
if (image != null) {
image.setOwner(feed);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index cc0a3d058..b1aff5594 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -4,11 +4,32 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.asynctask.FlattrStatusFetcher;
-import de.danoeh.antennapod.core.feed.*;
+import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -20,10 +41,6 @@ import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* Provides methods for doing common tasks that use DBReader and DBWriter.
*/
@@ -240,6 +257,49 @@ public final class DBTasks {
}
/**
+ * Downloads all pages of the given feed.
+ *
+ * @param context Used for requesting the download.
+ * @param feed The Feed object.
+ */
+ public static void refreshCompleteFeed(final Context context, final Feed feed) {
+ try {
+ refreshFeed(context, feed, true);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DBWriter.addDownloadStatus(
+ context,
+ new DownloadStatus(feed, feed
+ .getHumanReadableIdentifier(),
+ DownloadError.ERROR_REQUEST_ERROR, false, e
+ .getMessage()
+ )
+ );
+ }
+ }
+
+ /**
+ * Queues the next page of this Feed for download. The given Feed has to be a paged
+ * Feed (isPaged()=true) and must contain a nextPageLink.
+ *
+ * @param context Used for requesting the download.
+ * @param feed The feed whose next page should be loaded.
+ * @param loadAllPages True if any subsequent pages should also be loaded, false otherwise.
+ */
+ public static void loadNextPageOfFeed(final Context context, Feed feed, boolean loadAllPages) throws DownloadRequestException {
+ if (feed.isPaged() && feed.getNextPageLink() != null) {
+ int pageNr = feed.getPageNr() + 1;
+ Feed nextFeed = new Feed(feed.getNextPageLink(), new Date(), feed.getTitle() + "(" + pageNr + ")");
+ nextFeed.setPageNr(pageNr);
+ nextFeed.setPaged(true);
+ nextFeed.setId(feed.getId());
+ DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages);
+ } else {
+ Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink");
+ }
+ }
+
+ /**
* Updates a specific Feed.
*
* @param context Used for requesting the download.
@@ -247,6 +307,10 @@ public final class DBTasks {
*/
public static void refreshFeed(Context context, Feed feed)
throws DownloadRequestException {
+ refreshFeed(context, feed, false);
+ }
+
+ private static void refreshFeed(Context context, Feed feed, boolean loadAllPages) throws DownloadRequestException {
Feed f;
if (feed.getPreferences() == null) {
f = new Feed(feed.getDownload_url(), new Date(), feed.getTitle());
@@ -255,7 +319,7 @@ public final class DBTasks {
feed.getPreferences().getUsername(), feed.getPreferences().getPassword());
}
f.setId(feed.getId());
- DownloadRequester.getInstance().downloadFeed(context, f);
+ DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages);
}
/**
@@ -388,7 +452,7 @@ public final class DBTasks {
* 2. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
- * @param context Used for accessing the DB.
+ * @param context Used for accessing the DB.
* @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
* its media ID is in the mediaIds list.
* @return A Future that can be used for waiting for the methods completion.
@@ -687,11 +751,21 @@ public final class DBTasks {
+ " already exists. Syncing new with existing one.");
Collections.sort(newFeed.getItems(), new FeedItemPubdateComparator());
- if (savedFeed.compareWithOther(newFeed)) {
+
+ final boolean markNewItemsAsUnread;
+ if (newFeed.getPageNr() == savedFeed.getPageNr()) {
+ if (savedFeed.compareWithOther(newFeed)) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG,
+ "Feed has updated attribute values. Updating old feed's attributes");
+ savedFeed.updateFromOther(newFeed);
+ }
+ markNewItemsAsUnread = true;
+ } else {
if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Feed has updated attribute values. Updating old feed's attributes");
- savedFeed.updateFromOther(newFeed);
+ Log.d(TAG, "New feed has a higher page number. Merging without marking as unread");
+ markNewItemsAsUnread = false;
+ savedFeed.setNextPageLink(newFeed.getNextPageLink());
}
if (savedFeed.getPreferences().compareWithOther(newFeed.getPreferences())) {
if (BuildConfig.DEBUG)
@@ -708,7 +782,9 @@ public final class DBTasks {
final int i = idx;
item.setFeed(savedFeed);
savedFeed.getItems().add(i, item);
- item.setRead(false);
+ if (markNewItemsAsUnread) {
+ item.setRead(false);
+ }
} else {
oldItem.updateFromOther(item);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index f0331e997..148d886ae 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -2,15 +2,10 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.util.Log;
import android.webkit.URLUtil;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.*;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.download.DownloadRequest;
-import de.danoeh.antennapod.core.service.download.DownloadService;
-import de.danoeh.antennapod.core.util.FileNameGenerator;
-import de.danoeh.antennapod.core.util.URLChecker;
+
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@@ -19,6 +14,18 @@ import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import de.danoeh.antennapod.core.BuildConfig;
+import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.util.FileNameGenerator;
+import de.danoeh.antennapod.core.util.URLChecker;
+
/**
* Sends download requests to the DownloadService. This class should always be used for starting downloads,
@@ -31,6 +38,16 @@ public class DownloadRequester {
public static final String FEED_DOWNLOADPATH = "cache/";
public static final String MEDIA_DOWNLOADPATH = "media/";
+ /**
+ * Denotes the page of the feed that is contained in the DownloadRequest sent by the DownloadRequester.
+ */
+ public static final String REQUEST_ARG_PAGE_NR = "page";
+
+ /**
+ * True if all pages after the feed that is contained in this DownloadRequest should be downloaded.
+ */
+ public static final String REQUEST_ARG_LOAD_ALL_PAGES = "loadAllPages";
+
private static DownloadRequester downloader;
private Map<String, DownloadRequest> downloads;
@@ -74,7 +91,7 @@ public class DownloadRequester {
}
private void download(Context context, FeedFile item, File dest,
- boolean overwriteIfExists, String username, String password, boolean deleteOnFailure) {
+ boolean overwriteIfExists, String username, String password, boolean deleteOnFailure, Bundle arguments) {
if (!isDownloadingFile(item)) {
if (!isFilenameAvailable(dest.toString()) || (deleteOnFailure && dest.exists())) {
if (BuildConfig.DEBUG)
@@ -116,7 +133,7 @@ public class DownloadRequester {
DownloadRequest request = new DownloadRequest(dest.toString(),
URLChecker.prepareURL(item.getDownload_url()), item.getHumanReadableIdentifier(),
- item.getId(), item.getTypeAsInt(), username, password, deleteOnFailure);
+ item.getId(), item.getTypeAsInt(), username, password, deleteOnFailure, arguments);
download(context, request);
} else {
@@ -144,22 +161,30 @@ public class DownloadRequester {
return true;
}
- public synchronized void downloadFeed(Context context, Feed feed)
+ public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages)
throws DownloadRequestException {
if (feedFileValid(feed)) {
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
+ Bundle args = new Bundle();
+ args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
+ args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
+
download(context, feed, new File(getFeedfilePath(context),
- getFeedfileName(feed)), true, username, password, true);
+ getFeedfileName(feed)), true, username, password, true, args);
}
}
+ public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {
+ downloadFeed(context, feed, false);
+ }
+
public synchronized void downloadImage(Context context, FeedImage image)
throws DownloadRequestException {
if (feedFileValid(image)) {
download(context, image, new File(getImagefilePath(context),
- getImagefileName(image)), false, null, null, false);
+ getImagefileName(image)), false, null, null, false, null);
}
}
@@ -185,8 +210,7 @@ public class DownloadRequester {
getMediafilename(feedmedia));
}
download(context, feedmedia,
- dest, false, username, password, false
- );
+ dest, false, username, password, false, null);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index eab92eab2..79124521f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -67,6 +67,9 @@ public class PodDBAdapter {
public static final int KEY_FEED_FLATTR_STATUS_INDEX = 14;
public static final int KEY_FEED_USERNAME_INDEX = 15;
public static final int KEY_FEED_PASSWORD_INDEX = 16;
+ public static final int KEY_IS_PAGED_INDEX = 17;
+ public static final int KEY_LOAD_ALL_PAGES_INDEX = 18;
+ public static final int KEY_NEXT_PAGE_LINK_INDEX = 19;
// ----------- FeedItem indices
public static final int KEY_CONTENT_ENCODED_INDEX = 2;
public static final int KEY_PUBDATE_INDEX = 3;
@@ -144,6 +147,8 @@ public class PodDBAdapter {
public static final String KEY_PLAYED_DURATION = "played_duration";
public static final String KEY_USERNAME = "username";
public static final String KEY_PASSWORD = "password";
+ public static final String KEY_IS_PAGED = "is_paged";
+ public static final String KEY_NEXT_PAGE_LINK = "next_page_link";
// Table names
public static final String TABLE_NAME_FEEDS = "Feeds";
@@ -168,7 +173,10 @@ public class PodDBAdapter {
+ KEY_FEED_IDENTIFIER + " TEXT," + KEY_AUTO_DOWNLOAD + " INTEGER DEFAULT 1,"
+ KEY_FLATTR_STATUS + " INTEGER,"
+ KEY_USERNAME + " TEXT,"
- + KEY_PASSWORD + " TEXT)";
+ + KEY_PASSWORD + " TEXT,"
+ + KEY_IS_PAGED + " INTEGER DEFAULT 0,"
+ + KEY_NEXT_PAGE_LINK + " TEXT)";
+
public static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@@ -234,8 +242,10 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER,
TABLE_NAME_FEEDS + "." + KEY_AUTO_DOWNLOAD,
TABLE_NAME_FEEDS + "." + KEY_FLATTR_STATUS,
+ TABLE_NAME_FEEDS + "." + KEY_IS_PAGED,
+ TABLE_NAME_FEEDS + "." + KEY_NEXT_PAGE_LINK,
TABLE_NAME_FEEDS + "." + KEY_USERNAME,
- TABLE_NAME_FEEDS + "." + KEY_PASSWORD
+ TABLE_NAME_FEEDS + "." + KEY_PASSWORD,
};
// column indices for FEED_SEL_STD
@@ -255,8 +265,10 @@ public class PodDBAdapter {
public static final int IDX_FEED_SEL_STD_FEED_IDENTIFIER = 13;
public static final int IDX_FEED_SEL_PREFERENCES_AUTO_DOWNLOAD = 14;
public static final int IDX_FEED_SEL_STD_FLATTR_STATUS = 15;
- public static final int IDX_FEED_SEL_PREFERENCES_USERNAME = 16;
- public static final int IDX_FEED_SEL_PREFERENCES_PASSWORD = 17;
+ public static final int IDX_FEED_SEL_STD_IS_PAGED = 16;
+ public static final int IDX_FEED_SEL_STD_NEXT_PAGE_LINK = 17;
+ public static final int IDX_FEED_SEL_PREFERENCES_USERNAME = 18;
+ public static final int IDX_FEED_SEL_PREFERENCES_PASSWORD = 19;
/**
@@ -386,6 +398,8 @@ public class PodDBAdapter {
Log.d(TAG, "Setting feed with flattr status " + feed.getTitle() + ": " + feed.getFlattrStatus().toLong());
values.put(KEY_FLATTR_STATUS, feed.getFlattrStatus().toLong());
+ values.put(KEY_IS_PAGED, feed.isPaged());
+ values.put(KEY_NEXT_PAGE_LINK, feed.getNextPageLink());
if (feed.getId() == 0) {
// Create new entry
if (BuildConfig.DEBUG)
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
index 45d1413bf..f67721a6e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.syndication.handler;
-import de.danoeh.antennapod.core.feed.Feed;
-
import java.util.Map;
+import de.danoeh.antennapod.core.feed.Feed;
+
/**
* Container for results returned by the Feed parser
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
index 1de001c55..3928c65b3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.core.syndication.namespace.atom;
import android.util.Log;
+
+import org.xml.sax.Attributes;
+
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -12,7 +15,6 @@ import de.danoeh.antennapod.core.syndication.namespace.Namespace;
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils;
-import org.xml.sax.Attributes;
public class NSAtom extends Namespace {
private static final String TAG = "NSAtom";
@@ -44,6 +46,7 @@ public class NSAtom extends Namespace {
private static final String LINK_REL_PAYMENT = "payment";
private static final String LINK_REL_RELATED = "related";
private static final String LINK_REL_SELF = "self";
+ private static final String LINK_REL_NEXT = "next";
// type-values
private static final String LINK_TYPE_ATOM = "application/atom+xml";
private static final String LINK_TYPE_HTML = "text/html";
@@ -120,6 +123,9 @@ public class NSAtom extends Namespace {
}
} else if (rel.equals(LINK_REL_PAYMENT)) {
state.getFeed().setPaymentLink(href);
+ } else if (rel.equals(LINK_REL_NEXT)) {
+ state.getFeed().setPaged(true);
+ state.getFeed().setNextPageLink(href);
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java
new file mode 100644
index 000000000..d56871fd1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java
@@ -0,0 +1,56 @@
+package de.danoeh.antennapod.core.util.gui;
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+
+import de.danoeh.antennapod.core.R;
+
+/**
+ * Utility methods for the more_content_list_footer layout.
+ */
+public class MoreContentListFooterUtil {
+
+ private final View root;
+
+ private boolean loading;
+
+ private Listener listener;
+
+ public MoreContentListFooterUtil(View root) {
+ this.root = root;
+ root.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null && !loading) {
+ listener.onClick();
+ }
+ }
+ });
+ }
+
+ public void setLoadingState(boolean newState) {
+ final ImageView imageView = (ImageView) root.findViewById(R.id.imgExpand);
+ final ProgressBar progressBar = (ProgressBar) root.findViewById(R.id.progBar);
+ if (newState) {
+ imageView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ imageView.setVisibility(View.VISIBLE);
+ progressBar.setVisibility(View.GONE);
+ }
+ loading = newState;
+ }
+
+ public void setClickListener(Listener l) {
+ listener = l;
+ }
+
+ public static interface Listener {
+ public void onClick();
+ }
+
+ public View getRoot() {
+ return root;
+ }
+}
diff --git a/core/src/main/res/layout/more_content_list_footer.xml b/core/src/main/res/layout/more_content_list_footer.xml
new file mode 100644
index 000000000..b9947dc33
--- /dev/null
+++ b/core/src/main/res/layout/more_content_list_footer.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/imgExpand"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_margin="16dp"
+ android:contentDescription="@string/load_next_page_label"
+ android:src="?attr/navigation_expand" />
+
+ <ProgressBar
+ android:id="@+id/progBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_margin="16dp"
+ android:indeterminateOnly="true"
+ android:visibility="gone" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 6a62f5e14..f9b93f9c4 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -85,6 +85,7 @@
<string name="share_source_label">Share feed link</string>
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded.</string>
<string name="feed_remover_msg">Removing feed</string>
+ <string name="load_complete_feed">Load complete feed</string>
<!-- actions on feeditems -->
<string name="download_label">Download</string>
@@ -369,6 +370,7 @@
<string name="new_episodes_count_label">Number of new episodes</string>
<string name="in_progress_episodes_count_label">Number of episodes you have started listening to</string>
<string name="drag_handle_content_description">Drag to change the position of this item</string>
+ <string name="load_next_page_label">Load next page</string>
<!-- Feed information screen -->
<string name="authentication_label">Authentication</string>