From ad04a80ae7b2091c94b0d007c2a78d97e7af871d Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Mon, 10 Nov 2014 21:01:41 +0100 Subject: [PATCH] Squashed commit of the following: commit 7d0e6d5c2dc75f8f1e54f4a1279c6b0cffa7b8cc Author: daniel oeh Date: Mon Nov 10 21:00:58 2014 +0100 Made tests compile commit 05c57df87dd469d8f64835700eefe2e3c87e04e4 Author: daniel oeh Date: Mon Nov 10 20:56:58 2014 +0100 Removed unused code commit 0a1fbc9e6d9648646140e30dec0ec8389fb8d37f Author: daniel oeh Date: Thu Nov 6 15:48:24 2014 +0100 Added UI controls to download more feed pages closes #245 commit 6486fb40f1d03887e264df95946f91f0a9cdac9b Author: daniel oeh Date: Thu Nov 6 14:49:43 2014 +0100 Added support for downloading feed pages commit e1faa06908bfd50f2aa0c28ee5118772c4281557 Author: daniel oeh Date: Wed Nov 5 21:27:03 2014 +0100 Added "paged" and "loadAllPages" attributes --- .../service/download/HttpDownloaderTest.java | 2 +- .../test/antennapod/storage/DBReaderTest.java | 2 +- .../test/antennapod/storage/DBTasksTest.java | 2 +- .../test/antennapod/storage/DBTestUtils.java | 2 +- .../activity/OnlineFeedViewActivity.java | 4 +- .../config/StorageCallbacksImpl.java | 8 +- .../antennapod/fragment/ItemlistFragment.java | 43 +++++++- .../menuhandler/FeedMenuHandler.java | 5 + app/src/main/res/menu/feedlist.xml | 24 +++-- .../de/danoeh/antennapod/core/feed/Feed.java | 85 ++++++++++++--- .../service/download/DownloadRequest.java | 25 +++-- .../service/download/DownloadService.java | 78 +++++++++----- .../antennapod/core/storage/DBReader.java | 4 +- .../antennapod/core/storage/DBTasks.java | 100 +++++++++++++++--- .../core/storage/DownloadRequester.java | 52 ++++++--- .../antennapod/core/storage/PodDBAdapter.java | 22 +++- .../handler/FeedHandlerResult.java | 4 +- .../syndication/namespace/atom/NSAtom.java | 8 +- .../util/gui/MoreContentListFooterUtil.java | 56 ++++++++++ .../res/layout/more_content_list_footer.xml | 26 +++++ core/src/main/res/values/strings.xml | 2 + 21 files changed, 457 insertions(+), 97 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java create mode 100644 core/src/main/res/layout/more_content_list_footer.xml diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java index 1a561f282..65b1145a2 100644 --- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java +++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java @@ -65,7 +65,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase { private Downloader download(String url, String title, boolean expectedResult, boolean deleteExisting, String username, String password, boolean deleteOnFail) { FeedFile feedFile = setupFeedFile(url, title, deleteExisting); - DownloadRequest request = new DownloadRequest(feedFile.getFile_url(), url, title, 0, feedFile.getTypeAsInt(), username, password, deleteOnFail); + DownloadRequest request = new DownloadRequest(feedFile.getFile_url(), url, title, 0, feedFile.getTypeAsInt(), username, password, deleteOnFail, null); Downloader downloader = new HttpDownloader(request); downloader.call(); DownloadStatus status = downloader.getResult(); diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java index 33f7fb350..a96c9a6d3 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java @@ -43,7 +43,7 @@ public class DBReaderTest extends InstrumentationTestCase { private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) { final Context context = getInstrumentation().getTargetContext(); Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null, - null, null, null, "feed", null, null, "url", false, new FlattrStatus()); + null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null); feed.setItems(new ArrayList()); PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java index fd5b1c393..e28a7918f 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java @@ -296,7 +296,7 @@ public class DBTasksTest extends InstrumentationTestCase { final Context context = getInstrumentation().getTargetContext(); UserPreferences.setUpdateInterval(context, expirationTime); Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null, - null, null, null, "feed", null, null, "url", false, new FlattrStatus()); + null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null); feed.setItems(new ArrayList()); PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java index e7d6396f5..9e5f6546d 100644 --- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java +++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java @@ -32,7 +32,7 @@ public class DBTestUtils { adapter.open(); for (int i = 0; i < numFeeds; i++) { Feed f = new Feed(0, new Date(), "feed " + i, "link" + i, "descr", null, null, - null, null, "id" + i, null, null, "url" + i, false, new FlattrStatus()); + null, null, "id" + i, null, null, "url" + i, false, new FlattrStatus(), false, null); f.setItems(new ArrayList()); for (int j = 0; j < numItems; j++) { FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(), diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index d84e6cc03..9f028000e 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -183,14 +183,14 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity { url = URLChecker.prepareURL(url); feed = new Feed(url, new Date()); if (username != null && password != null) { - feed.setPreferences(new FeedPreferences(0, true, username, password)); + feed.setPreferences(new FeedPreferences(0, false, username, password)); } String fileUrl = new File(getExternalCacheDir(), FileNameGenerator.generateFileName(feed.getDownload_url())) .toString(); feed.setFile_url(fileUrl); final DownloadRequest request = new DownloadRequest(feed.getFile_url(), - feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password, true); + feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password, true, null); downloader = new HttpDownloader( request); new Thread() { diff --git a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java index ec133aed1..ebb3780b7 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java @@ -13,7 +13,7 @@ public class StorageCallbacksImpl implements StorageCallbacks { @Override public int getDatabaseVersion() { - return 12; + return 13; } @Override @@ -103,5 +103,11 @@ public class StorageCallbacksImpl implements StorageCallbacks { + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE + " INTEGER"); } + if (oldVersion <= 12) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT"); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index 15f6e85e4..abf894c2a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; @@ -44,9 +43,11 @@ import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.QueueAccess; +import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil; import de.danoeh.antennapod.dialog.FeedItemDialog; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; @@ -82,6 +83,8 @@ public class ItemlistFragment extends ListFragment { private FeedItemDialog feedItemDialog; private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance; + private MoreContentListFooterUtil listFooter; + private boolean isUpdatingFeed; /** @@ -293,12 +296,17 @@ public class ItemlistFragment extends ListFragment { if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) { getActivity().supportInvalidateOptionsMenu(); } + if (listFooter != null) { + listFooter.setLoadingState(DownloadRequester.getInstance().isDownloadingFeeds()); + } + } private void onFragmentLoaded() { if (adapter == null) { getListView().setAdapter(null); setupHeaderView(); + setupFooterView(); adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false); setListAdapter(adapter); downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback); @@ -313,6 +321,11 @@ public class ItemlistFragment extends ListFragment { feedItemDialog = FeedItemDialog.newInstance(getActivity(), feedItemDialogSavedInstance); } getActivity().supportInvalidateOptionsMenu(); + + if (feed != null && feed.getNextPageLink() == null && listFooter != null) { + getListView().removeFooterView(listFooter.getRoot()); + } + } private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { @@ -372,6 +385,34 @@ public class ItemlistFragment extends ListFragment { }); } + private void setupFooterView() { + if (getListView() == null || feed == null) { + Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + return; + } + if (feed.isPaged() && feed.getNextPageLink() != null) { + ListView lv = getListView(); + LayoutInflater inflater = (LayoutInflater) + getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View header = inflater.inflate(R.layout.more_content_list_footer, lv, false); + lv.addFooterView(header); + listFooter = new MoreContentListFooterUtil(header); + listFooter.setClickListener(new MoreContentListFooterUtil.Listener() { + @Override + public void onClick() { + if (feed != null) { + try { + DBTasks.loadNextPageOfFeed(getActivity(), feed, false); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); + } + } + } + }); + } + } + private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index e382a5888..b62fd22b2 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -41,6 +41,8 @@ public class FeedMenuHandler { else menu.findItem(R.id.support_item).setVisible(false); + menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged()); + return true; } @@ -55,6 +57,9 @@ public class FeedMenuHandler { case R.id.refresh_item: DBTasks.refreshFeed(context, selectedFeed); break; + case R.id.refresh_complete_item: + DBTasks.refreshCompleteFeed(context, selectedFeed); + break; case R.id.mark_all_read_item: DBWriter.markFeedRead(context, selectedFeed.getId()); break; diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml index dfee70522..8d2d9e367 100644 --- a/app/src/main/res/menu/feedlist.xml +++ b/app/src/main/res/menu/feedlist.xml @@ -6,29 +6,35 @@ android:id="@+id/refresh_item" android:icon="?attr/navigation_refresh" android:menuCategory="container" - custom:showAsAction="ifRoom" - android:title="@string/refresh_label"> + android:title="@string/refresh_label" + custom:showAsAction="ifRoom"> + + + android:title="@string/mark_all_read_label" + custom:showAsAction="collapseActionView"> + android:visible="false" + custom:showAsAction="collapseActionView"> + android:visible="true" + custom:showAsAction="collapseActionView"> \ No newline at end of file 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 * @@ -58,12 +60,33 @@ 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". + *

+ * 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(); } @@ -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 completedRequests = new LinkedBlockingDeque(); - private CompletionService parserService = new ExecutorCompletionService(Executors.newSingleThreadExecutor()); + private CompletionService> parserService = new ExecutorCompletionService>(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 collectCompletedRequests() { - List results = new LinkedList(); + private List> collectCompletedRequests() { + List> results = new LinkedList>(); 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 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 feeds = collectCompletedRequests(); + final List> 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 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 { + /** + * Helper method + */ + private Feed[] getFeeds(List> 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> { private DownloadRequest request; @@ -854,27 +880,28 @@ public class DownloadService extends Service { } @Override - public Feed call() throws Exception { + public Pair call() throws Exception { return parseFeed(request); } } - private Feed parseFeed(DownloadRequest request) { - Feed savedFeed = null; - + private Pair 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. */ @@ -239,6 +256,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. * @@ -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 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 @@ + + + + + + + + \ 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 @@ Share feed link Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded. Removing feed + Load complete feed Download @@ -369,6 +370,7 @@ Number of new episodes Number of episodes you have started listening to Drag to change the position of this item + Load next page Authentication