summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
authorTom Hennen <TomHennen@users.noreply.github.com>2015-05-20 18:20:03 -0400
committerTom Hennen <TomHennen@users.noreply.github.com>2015-05-20 18:20:03 -0400
commitba036e14990a6dd7e8a5076bdc20176532a4f417 (patch)
tree735d3c244c2e5a1266eb740521bf923c8264726c /core/src/main
parent3301342a761f614370a15afabeaf28a0b364913a (diff)
parent25cbb00b36df1e9378ed84478fa494e6d253b5dd (diff)
downloadAntennaPod-ba036e14990a6dd7e8a5076bdc20176532a4f417.zip
Merge pull request #808 from mfietz/feature/nav-indicators+new-redone
Nav indicators, context menus and new "new"
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java43
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java93
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java52
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java136
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntList.java240
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java252
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LongList.java1
-rw-r--r--core/src/main/res/values/strings.xml12
12 files changed, 767 insertions, 106 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 ca87066fe..29ba721fe 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
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.feed;
import android.content.Context;
import android.net.Uri;
+import android.support.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
@@ -79,6 +80,8 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
*/
private String nextPageLink;
+ private boolean lastUpdateFailed;
+
/**
* Contains property strings. If such a property applies to a feed item, it is not shown in the feed list
*/
@@ -90,7 +93,7 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
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, boolean paged, String nextPageLink,
- String filter) {
+ String filter, boolean lastUpdateFailed) {
super(fileUrl, downloadUrl, downloaded);
this.id = id;
this.title = title;
@@ -110,13 +113,13 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
this.flattrStatus = status;
this.paged = paged;
this.nextPageLink = nextPageLink;
+ this.items = new ArrayList<FeedItem>();
if(filter != null) {
this.itemfilter = new FeedItemFilter(filter);
} else {
this.itemfilter = new FeedItemFilter(new String[0]);
}
-
- items = new ArrayList<FeedItem>();
+ this.lastUpdateFailed = lastUpdateFailed;
}
/**
@@ -126,7 +129,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(), false, null, null);
+ fileUrl, downloadUrl, downloaded, new FlattrStatus(), false, null, null, false);
}
/**
@@ -134,7 +137,6 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
*/
public Feed() {
super();
- items = new ArrayList<FeedItem>();
lastUpdate = new Date();
this.flattrStatus = new FlattrStatus();
}
@@ -172,13 +174,10 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
/**
* Returns true if at least one item in the itemlist is unread.
*
- * @param enableEpisodeFilter true if this method should only count items with episodes if
- * the 'display only episodes' - preference is set to true by the
- * user.
*/
- public boolean hasNewItems(boolean enableEpisodeFilter) {
+ public boolean hasNewItems() {
for (FeedItem item : items) {
- if (item.getState() == FeedItem.State.NEW) {
+ if (item.getState() == FeedItem.State.UNREAD) {
if (item.getMedia() != null) {
return true;
}
@@ -190,21 +189,16 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
/**
* Returns the number of FeedItems.
*
- * @param enableEpisodeFilter true if this method should only count items with episodes if
- * the 'display only episodes' - preference is set to true by the
- * user.
*/
- public int getNumOfItems(boolean enableEpisodeFilter) {
+ public int getNumOfItems() {
return items.size();
}
/**
* Returns the item at the specified index.
*
- * @param enableEpisodeFilter true if this method should ignore items without episdodes if
- * the episodes filter has been enabled by the user.
*/
- public FeedItem getItemAtIndex(boolean enableEpisodeFilter, int position) {
+ public FeedItem getItemAtIndex(int position) {
return items.get(position);
}
@@ -483,14 +477,23 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
this.nextPageLink = nextPageLink;
}
+ @Nullable
public FeedItemFilter getItemFilter() {
return itemfilter;
}
- public void setFeedItemsFilter(String[] filter) {
- if(filter != null) {
- this.itemfilter = new FeedItemFilter(filter);
+ public void setHiddenItemProperties(String[] properties) {
+ if (properties != null) {
+ this.itemfilter = new FeedItemFilter(properties);
}
}
+ public boolean hasLastUpdateFailed() {
+ return this.lastUpdateFailed;
+ }
+
+ public void setLastUpdateFailed(boolean lastUpdateFailed) {
+ this.lastUpdateFailed = lastUpdateFailed;
+ }
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
index 45a62ca12..11348953e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
@@ -239,7 +239,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
}
public boolean isRead() {
- return read || isInProgress();
+ return read;
}
public void setRead(boolean read) {
@@ -330,7 +330,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
}
public enum State {
- NEW, IN_PROGRESS, READ, PLAYING
+ UNREAD, IN_PROGRESS, READ, PLAYING
}
public State getState() {
@@ -342,7 +342,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
return State.IN_PROGRESS;
}
}
- return (isRead() ? State.READ : State.NEW);
+ return (isRead() ? State.READ : State.UNREAD);
}
public long getFeedId() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
index be84eacc7..4ad084b39 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
@@ -11,7 +11,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
public class FeedItemFilter {
- private final String[] filter;
+ private final String[] properties;
private boolean hideUnplayed = false;
private boolean hidePaused = false;
@@ -21,15 +21,15 @@ public class FeedItemFilter {
private boolean hideDownloaded = false;
private boolean hideNotDownloaded = false;
- public FeedItemFilter(String filter) {
- this(StringUtils.split(filter, ','));
+ public FeedItemFilter(String properties) {
+ this(StringUtils.split(properties, ','));
}
- public FeedItemFilter(String[] filter) {
- this.filter = filter;
- for(String f : filter) {
+ public FeedItemFilter(String[] properties) {
+ this.properties = properties;
+ for(String property : properties) {
// see R.arrays.feed_filter_values
- switch(f) {
+ switch(property) {
case "unplayed":
hideUnplayed = true;
break;
@@ -56,7 +56,7 @@ public class FeedItemFilter {
}
public List<FeedItem> filter(Context context, List<FeedItem> items) {
- if(filter.length == 0) {
+ if(properties.length == 0) {
return items;
}
List<FeedItem> result = new ArrayList<FeedItem>();
@@ -76,7 +76,7 @@ public class FeedItemFilter {
}
public String[] getValues() {
- return filter.clone();
+ return properties.clone();
}
}
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 4e40fbe1e..e7b226eca 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
@@ -1060,9 +1060,11 @@ public class DownloadService extends Service {
@Override
public void run() {
- if (request.isDeleteOnFailure()) {
+ if(request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
+ DBWriter.setFeedLastUpdateFailed(DownloadService.this, request.getFeedfileId(), true);
+ } else if (request.isDeleteOnFailure()) {
Log.d(TAG, "Ignoring failed download, deleteOnFailure=true");
- } else {
+ } else {
File dest = new File(request.getDestination());
if (dest.exists() && request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
Log.d(TAG, "File has been partially downloaded. Writing file url");
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 393659c7d..cc20b3d37 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
@@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -23,6 +24,7 @@ import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.DownloadError;
+import de.danoeh.antennapod.core.util.LongIntMap;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
@@ -176,8 +178,7 @@ public final class DBReader {
*/
public static List<FeedItem> getFeedItemList(Context context,
final Feed feed) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Extracting Feeditems of feed " + feed.getTitle());
+ Log.d(TAG, "Extracting Feeditems of feed " + feed.getTitle());
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
@@ -317,7 +318,8 @@ public final class DBReader {
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),
- cursor.getString(cursor.getColumnIndex(PodDBAdapter.KEY_HIDE))
+ cursor.getString(cursor.getColumnIndex(PodDBAdapter.KEY_HIDE)),
+ cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED)) > 0
);
if (image != null) {
@@ -489,21 +491,45 @@ public final class DBReader {
}
/**
+ * Loads a list of FeedItems that are considered new.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @return A list of FeedItems that are considered new.
+ */
+ public static List<FeedItem> getNewItemsList(Context context) {
+ Log.d(TAG, "getNewItemsList()");
+
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+
+ Cursor itemlistCursor = adapter.getNewItemsCursor();
+ List<FeedItem> items = extractItemlistFromCursor(adapter, itemlistCursor);
+ itemlistCursor.close();
+
+ loadFeedDataOfFeedItemlist(context, items);
+
+ adapter.close();
+
+ return items;
+ }
+
+ /**
* Loads the IDs of the FeedItems whose 'read'-attribute is set to false.
*
* @param context A context that is used for opening a database connection.
* @return A list of IDs of the FeedItems whose 'read'-attribute is set to false. This method should be preferred
* over {@link #getUnreadItemsList(android.content.Context)} if the FeedItems in the UnreadItems list are not used.
*/
- public static long[] getUnreadItemIds(Context context) {
+ public static LongList getNewItemIds(Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
- Cursor cursor = adapter.getUnreadItemIdsCursor();
- long[] itemIds = new long[cursor.getCount()];
+ Cursor cursor = adapter.getNewItemIdsCursor();
+ LongList itemIds = new LongList(cursor.getCount());
int i = 0;
if (cursor.moveToFirst()) {
do {
- itemIds[i] = cursor.getLong(PodDBAdapter.KEY_ID_INDEX);
+ long id = cursor.getLong(PodDBAdapter.KEY_ID_INDEX);
+ itemIds.add(id);
i++;
} while (cursor.moveToNext());
}
@@ -956,10 +982,24 @@ public final class DBReader {
* @param context A context that is used for opening a database connection.
* @return The number of unread items.
*/
- public static int getNumberOfUnreadItems(final Context context) {
+ public static int getNumberOfNewItems(final Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
- final int result = adapter.getNumberOfUnreadItems();
+ final int result = adapter.getNumberOfNewItems();
+ adapter.close();
+ return result;
+ }
+
+ /**
+ * Returns a map containing the number of unread items per feed
+ *
+ * @param context A context that is used for opening a database connection.
+ * @return The number of unread items per feed.
+ */
+ public static LongIntMap getNumberOfUnreadFeedItems(final Context context, long... feedIds) {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ final LongIntMap result = adapter.getNumberOfUnreadFeedItems(feedIds);
adapter.close();
return result;
}
@@ -1088,9 +1128,31 @@ public final class DBReader {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
List<Feed> feeds = getFeedList(adapter);
+ long[] feedIds = new long[feeds.size()];
+ for(int i=0; i < feeds.size(); i++) {
+ feedIds[i] = feeds.get(i).getId();
+ }
+ final LongIntMap numUnreadFeedItems = adapter.getNumberOfUnreadFeedItems(feedIds);
+ Collections.sort(feeds, new Comparator<Feed>() {
+ @Override
+ public int compare(Feed lhs, Feed rhs) {
+ long numUnreadLhs = numUnreadFeedItems.get(lhs.getId());
+ Log.d(TAG, "feed with id " + lhs.getId() + " has " + numUnreadLhs + " unread items");
+ long numUnreadRhs = numUnreadFeedItems.get(rhs.getId());
+ Log.d(TAG, "feed with id " + rhs.getId() + " has " + numUnreadRhs + " unread items");
+ if(numUnreadLhs > numUnreadRhs) {
+ // reverse natural order: podcast with most unplayed episodes first
+ return -1;
+ } else if(numUnreadLhs == numUnreadRhs) {
+ return lhs.getTitle().compareTo(rhs.getTitle());
+ } else {
+ return 1;
+ }
+ }
+ });
int queueSize = adapter.getQueueSize();
- int numUnreadItems = adapter.getNumberOfUnreadItems();
- NavDrawerData result = new NavDrawerData(feeds, queueSize, numUnreadItems);
+ int numNewItems = adapter.getNumberOfNewItems();
+ NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numUnreadFeedItems);
adapter.close();
return result;
}
@@ -1098,12 +1160,15 @@ public final class DBReader {
public static class NavDrawerData {
public List<Feed> feeds;
public int queueSize;
- public int numUnreadItems;
+ public int numNewItems;
+ public LongIntMap numUnreadFeedItems;
- public NavDrawerData(List<Feed> feeds, int queueSize, int numUnreadItems) {
+ public NavDrawerData(List<Feed> feeds, int queueSize, int numNewItems,
+ LongIntMap numUnreadFeedItems) {
this.feeds = feeds;
this.queueSize = queueSize;
- this.numUnreadItems = numUnreadItems;
+ this.numNewItems = numNewItems;
+ this.numUnreadFeedItems = numUnreadFeedItems;
}
}
}
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 ecbfce5e0..e570ee709 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
@@ -20,7 +20,6 @@ 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;
@@ -221,8 +220,7 @@ public final class DBTasks {
* @param context Used for DB access.
*/
public static void refreshExpiredFeeds(final Context context) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Refreshing expired feeds");
+ Log.d(TAG, "Refreshing expired feeds");
new Thread() {
public void run() {
@@ -304,6 +302,7 @@ public final class DBTasks {
*/
public static void refreshFeed(Context context, Feed feed)
throws DownloadRequestException {
+ Log.d(TAG, "id " + feed.getId());
refreshFeed(context, feed, false);
}
@@ -458,14 +457,6 @@ public final class DBTasks {
}
/**
- * Adds all FeedItem objects whose 'read'-attribute is false to the queue in a separate thread.
- */
- public static void enqueueAllNewItems(final Context context) {
- long[] unreadItems = DBReader.getUnreadItemIds(context);
- DBWriter.addQueueItem(context, unreadItems);
- }
-
- /**
* Returns the successor of a FeedItem in the queue.
*
* @param context Used for accessing the DB.
@@ -620,6 +611,7 @@ public final class DBTasks {
// update attributes
savedFeed.setLastUpdate(newFeed.getLastUpdate());
savedFeed.setType(newFeed.getType());
+ savedFeed.setLastUpdateFailed(false);
updatedFeedsList.add(savedFeed);
resultFeeds[feedIdx] = savedFeed;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index b9a61e62b..fe5d0dfd3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -354,30 +354,16 @@ public class DBWriter {
FeedItem item = null;
if (queue != null) {
- boolean queueModified = false;
- boolean unreadItemsModified = false;
-
if (!itemListContains(queue, itemId)) {
item = DBReader.getFeedItem(context, itemId);
if (item != null) {
queue.add(index, item);
- queueModified = true;
- if (!item.isRead()) {
- item.setRead(true);
- unreadItemsModified = true;
- }
+ adapter.setQueue(queue);
+ EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, index));
}
}
- if (queueModified) {
- adapter.setQueue(queue);
- EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, index));
- }
- if (unreadItemsModified && item != null) {
- adapter.setSingleFeedItem(item);
- EventDistributor.getInstance()
- .sendUnreadItemsUpdateBroadcast();
- }
}
+
adapter.close();
if (performAutoDownload) {
DBTasks.autodownloadUndownloadedItems(context);
@@ -422,16 +408,11 @@ public class DBWriter {
if(addToFront){
queue.add(0, item);
- }else{
+ } else {
queue.add(item);
}
queueModified = true;
- if (!item.isRead()) {
- item.setRead(true);
- itemsToSave.add(item);
- unreadItemsModified = true;
- }
}
}
}
@@ -439,11 +420,6 @@ public class DBWriter {
adapter.setQueue(queue);
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
}
- if (unreadItemsModified) {
- adapter.setFeedItemlist(itemsToSave);
- EventDistributor.getInstance()
- .sendUnreadItemsUpdateBroadcast();
- }
}
adapter.close();
DBTasks.autodownloadUndownloadedItems(context);
@@ -936,6 +912,26 @@ public class DBWriter {
}
/**
+ * Saves if a feed's last update failed
+ *
+ * @param lastUpdateFailed true if last update failed
+ */
+ public static Future<?> setFeedLastUpdateFailed(final Context context,
+ final long feedId,
+ final boolean lastUpdateFailed) {
+ return dbExec.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ adapter.setFeedLastUpdateFailed(feedId, lastUpdateFailed);
+ adapter.close();
+ }
+ });
+ }
+
+ /**
* format an url for querying the database
* (postfix a / and apply percent-encoding)
*/
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 0bae7cf8e..4780098e0 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
@@ -27,8 +27,11 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.util.LongIntMap;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
+;
+
// TODO Remove media column from feeditem table
/**
@@ -151,6 +154,7 @@ public class PodDBAdapter {
public static final String KEY_IS_PAGED = "is_paged";
public static final String KEY_NEXT_PAGE_LINK = "next_page_link";
public static final String KEY_HIDE = "hide";
+ public static final String KEY_LAST_UPDATE_FAILED = "last_update_failed";
// Table names
public static final String TABLE_NAME_FEEDS = "Feeds";
@@ -178,8 +182,8 @@ public class PodDBAdapter {
+ KEY_PASSWORD + " TEXT,"
+ KEY_IS_PAGED + " INTEGER DEFAULT 0,"
+ KEY_NEXT_PAGE_LINK + " TEXT,"
- + KEY_HIDE + " TEXT)";
-
+ + KEY_HIDE + " TEXT,"
+ + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0)";
public static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@@ -204,7 +208,8 @@ public class PodDBAdapter {
+ " INTEGER," + KEY_SIZE + " INTEGER," + KEY_MIME_TYPE + " TEXT,"
+ KEY_PLAYBACK_COMPLETION_DATE + " INTEGER,"
+ KEY_FEEDITEM + " INTEGER,"
- + KEY_PLAYED_DURATION + " INTEGER)";
+ + KEY_PLAYED_DURATION + " INTEGER,"
+ + KEY_AUTO_DOWNLOAD + " INTEGER)";
public static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE "
+ TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE
@@ -222,6 +227,28 @@ public class PodDBAdapter {
+ " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER,"
+ KEY_LINK + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
+ // SQL Statements for creating indexes
+ public static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX "
+ + TABLE_NAME_FEED_ITEMS + "_" + KEY_FEED + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ + KEY_FEED + ")";
+
+ public static final String CREATE_INDEX_FEEDITEMS_IMAGE = "CREATE INDEX "
+ + TABLE_NAME_FEED_ITEMS + "_" + KEY_IMAGE + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ + KEY_IMAGE + ")";
+
+ public static final String CREATE_INDEX_QUEUE_FEEDITEM = "CREATE INDEX "
+ + TABLE_NAME_QUEUE + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_QUEUE + " ("
+ + KEY_FEEDITEM + ")";
+
+ public static final String CREATE_INDEX_FEEDMEDIA_FEEDITEM = "CREATE INDEX "
+ + TABLE_NAME_FEED_MEDIA + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_FEED_MEDIA + " ("
+ + KEY_FEEDITEM + ")";
+
+ public static final String CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM = "CREATE INDEX "
+ + TABLE_NAME_SIMPLECHAPTERS + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_SIMPLECHAPTERS + " ("
+ + KEY_FEEDITEM + ")";
+
+
private SQLiteDatabase db;
private final Context context;
private PodDBHelper helper;
@@ -250,7 +277,8 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_NEXT_PAGE_LINK,
TABLE_NAME_FEEDS + "." + KEY_USERNAME,
TABLE_NAME_FEEDS + "." + KEY_PASSWORD,
- TABLE_NAME_FEEDS + "." + KEY_HIDE
+ TABLE_NAME_FEEDS + "." + KEY_HIDE,
+ TABLE_NAME_FEEDS + "." + KEY_LAST_UPDATE_FAILED,
};
// column indices for FEED_SEL_STD
@@ -275,7 +303,6 @@ public class PodDBAdapter {
public static final int IDX_FEED_SEL_PREFERENCES_USERNAME = 18;
public static final int IDX_FEED_SEL_PREFERENCES_PASSWORD = 19;
-
/**
* Select all columns from the feeditems-table except description and
* content-encoded.
@@ -407,7 +434,12 @@ public class PodDBAdapter {
values.put(KEY_FLATTR_STATUS, feed.getFlattrStatus().toLong());
values.put(KEY_IS_PAGED, feed.isPaged());
values.put(KEY_NEXT_PAGE_LINK, feed.getNextPageLink());
- values.put(KEY_HIDE, StringUtils.join(feed.getItemFilter(), ","));
+ if(feed.getItemFilter() != null && feed.getItemFilter().getValues().length > 0) {
+ values.put(KEY_HIDE, StringUtils.join(feed.getItemFilter().getValues(), ","));
+ } else {
+ values.put(KEY_HIDE, "");
+ }
+ values.put(KEY_LAST_UPDATE_FAILED, feed.hasLastUpdateFailed());
if (feed.getId() == 0) {
// Create new entry
Log.d(this.toString(), "Inserting new Feed into db");
@@ -779,6 +811,13 @@ public class PodDBAdapter {
}
}
+ public void setFeedLastUpdateFailed(long feedId, boolean failed) {
+ final String sql = "UPDATE " + TABLE_NAME_FEEDS
+ + " SET " + KEY_LAST_UPDATE_FAILED+ "=" + (failed ? "1" : "0")
+ + " WHERE " + KEY_ID + "="+ feedId;
+ db.execSQL(sql);
+ }
+
/**
* Inserts or updates a download status.
*/
@@ -1049,11 +1088,43 @@ public class PodDBAdapter {
return c;
}
- public final Cursor getUnreadItemIdsCursor() {
- Cursor c = db.query(TABLE_NAME_FEED_ITEMS, new String[]{KEY_ID},
- KEY_READ + "=0", null, null, null, KEY_PUBDATE + " DESC");
- return c;
+ public final Cursor getNewItemIdsCursor() {
+ final String query = "SELECT " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
+ + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
+ + " WHERE "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
+ + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
+ return db.rawQuery(query, null);
+ }
+ /**
+ * Returns a cursor which contains all feed items that are considered new.
+ * The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection.
+ */
+ public final Cursor getNewItemsCursor() {
+ final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
+ + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
+ + " WHERE "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
+ + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL" // not in queue
+ + " ORDER BY " + KEY_PUBDATE + " DESC";
+ Cursor c = db.rawQuery(query, null);
+ return c;
}
public final Cursor getRecentlyPublishedItemsCursor(int limit) {
@@ -1149,7 +1220,7 @@ public class PodDBAdapter {
}
public final Cursor getFeedItemCursor(final String id) {
- return getFeedItemCursor(new String[] { id });
+ return getFeedItemCursor(new String[]{id});
}
public final Cursor getFeedItemCursor(final String[] ids) {
@@ -1199,9 +1270,20 @@ public class PodDBAdapter {
return result;
}
- public final int getNumberOfUnreadItems() {
- final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_ITEMS +
- " WHERE " + KEY_READ + " = 0";
+ public final int getNumberOfNewItems() {
+ final String query = "SELECT COUNT(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ")"
+ +" FROM " + TABLE_NAME_FEED_ITEMS
+ + " LEFT JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
+ + " LEFT JOIN " + TABLE_NAME_QUEUE + " ON "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
+ + " WHERE "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
+ + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
+ + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
Cursor c = db.rawQuery(query, null);
int result = 0;
if (c.moveToFirst()) {
@@ -1211,6 +1293,25 @@ public class PodDBAdapter {
return result;
}
+ public final LongIntMap getNumberOfUnreadFeedItems(long... feedIds) {
+ final String query = "SELECT " + KEY_FEED + ", COUNT(" + KEY_ID + ") AS count "
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " WHERE " + KEY_FEED + " IN (" + StringUtils.join(feedIds, ',') + ") "
+ + " AND " + KEY_READ + " = 0"
+ + " GROUP BY " + KEY_FEED;
+ Cursor c = db.rawQuery(query, null);
+ LongIntMap result = new LongIntMap(c.getCount());
+ if (c.moveToFirst()) {
+ do {
+ long feedId = c.getLong(0);
+ int count = c.getInt(1);
+ result.put(feedId, count);
+ } while(c.moveToNext());
+ }
+ c.close();
+ return result;
+ }
+
public final int getNumberOfDownloadedEpisodes() {
final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_MEDIA +
" WHERE " + KEY_DOWNLOADED + " > 0";
@@ -1372,6 +1473,13 @@ public class PodDBAdapter {
db.execSQL(CREATE_TABLE_DOWNLOAD_LOG);
db.execSQL(CREATE_TABLE_QUEUE);
db.execSQL(CREATE_TABLE_SIMPLECHAPTERS);
+
+ db.execSQL(CREATE_INDEX_FEEDITEMS_FEED);
+ db.execSQL(CREATE_INDEX_FEEDITEMS_IMAGE);
+ db.execSQL(CREATE_INDEX_FEEDMEDIA_FEEDITEM);
+ db.execSQL(CREATE_INDEX_QUEUE_FEEDITEM);
+ db.execSQL(CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
+
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java
new file mode 100644
index 000000000..673c81235
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java
@@ -0,0 +1,240 @@
+package de.danoeh.antennapod.core.util;
+
+import java.util.Arrays;
+
+/**
+ * Fast and memory efficient int list
+ */
+public final class IntList {
+
+ private int[] values;
+ protected int size;
+
+ /**
+ * Constructs an empty instance with a default initial capacity.
+ */
+ public IntList() {
+ this(4);
+ }
+
+ /**
+ * Constructs an empty instance.
+ *
+ * @param initialCapacity {@code >= 0;} initial capacity of the list
+ */
+ public IntList(int initialCapacity) {
+ if(initialCapacity < 0) {
+ throw new IllegalArgumentException("initial capacity must be 0 or higher");
+ }
+ values = new int[initialCapacity];
+ size = 0;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 1;
+ for (int i = 0; i < size; i++) {
+ int value = values[i];
+ hashCode = 31 * hashCode + (int)(value ^ (value >>> 32));
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (! (other instanceof IntList)) {
+ return false;
+ }
+ IntList otherList = (IntList) other;
+ if (size != otherList.size) {
+ return false;
+ }
+ for (int i = 0; i < size; i++) {
+ if (values[i] != otherList.values[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(size * 5 + 10);
+ sb.append("IntList{");
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(values[i]);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Gets the number of elements in this list.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Gets the indicated value.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return the indicated element's value
+ */
+ public int get(int n) {
+ if (n >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ } else if(n < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ return values[n];
+ }
+
+ /**
+ * Sets the value at the given index.
+ *
+ * @param index the index at which to put the specified object.
+ * @param value the object to add.
+ * @return the previous element at the index.
+ */
+ public int set(int index, int value) {
+ if (index >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ } else if(index < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ int result = values[index];
+ values[index] = value;
+ return result;
+ }
+
+ /**
+ * Adds an element to the end of the list. This will increase the
+ * list's capacity if necessary.
+ *
+ * @param value the value to add
+ */
+ public void add(int value) {
+ growIfNeeded();
+ values[size++] = value;
+ }
+
+ /**
+ * Inserts element into specified index, moving elements at and above
+ * that index up one. May not be used to insert at an index beyond the
+ * current size (that is, insertion as a last element is legal but
+ * no further).
+ *
+ * @param n {@code >= 0, <=size();} index of where to insert
+ * @param value value to insert
+ */
+ public void insert(int n, int value) {
+ if (n > size) {
+ throw new IndexOutOfBoundsException("n > size()");
+ } else if(n < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+
+ growIfNeeded();
+
+ System.arraycopy (values, n, values, n+1, size - n);
+ values[n] = value;
+ size++;
+ }
+
+ /**
+ * Removes value from this list.
+ *
+ * @param value value to remove
+ * return {@code true} if the value was removed, {@code false} otherwise
+ */
+ public boolean remove(int value) {
+ for (int i = 0; i < size; i++) {
+ if (values[i] == value) {
+ size--;
+ System.arraycopy(values, i+1, values, i, size-i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes an element at a given index, shifting elements at greater
+ * indicies down one.
+ *
+ * @param index index of element to remove
+ */
+ public void removeIndex(int index) {
+ if (index >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ } else if(index < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ size--;
+ System.arraycopy (values, index + 1, values, index, size - index);
+ }
+
+ /**
+ * Increases size of array if needed
+ */
+ private void growIfNeeded() {
+ if (size == values.length) {
+ // Resize.
+ int[] newArray = new int[size * 3 / 2 + 10];
+ System.arraycopy(values, 0, newArray, 0, size);
+ values = newArray;
+ }
+ }
+
+ /**
+ * Returns the index of the given value, or -1 if the value does not
+ * appear in the list.
+ *
+ * @param value value to find
+ * @return index of value or -1
+ */
+ public int indexOf(int value) {
+ for (int i = 0; i < size; i++) {
+ if (values[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all values from this list.
+ */
+ public void clear() {
+ values = new int[4];
+ size = 0;
+ }
+
+
+ /**
+ * Returns true if the given value is contained in the list
+ *
+ * @param value value to look for
+ * @return {@code true} if this list contains {@code value}, {@code false} otherwise
+ */
+ public boolean contains(int value) {
+ return indexOf(value) >= 0;
+ }
+
+ /**
+ * Returns an array with a copy of this list's values
+ *
+ * @return array with a copy of this list's values
+ */
+ public int[] toArray() {
+ return Arrays.copyOf(values, size);
+
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java b/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java
new file mode 100644
index 000000000..33fd252eb
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java
@@ -0,0 +1,252 @@
+package de.danoeh.antennapod.core.util;
+
+
+/**
+ * Fast and memory efficient long to long map
+ */
+public class LongIntMap {
+
+ private long[] keys;
+ private int[] values;
+ private int size;
+
+ /**
+ * Creates a new LongLongMap containing no mappings.
+ */
+ public LongIntMap() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseLongArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
+ */
+ public LongIntMap(int initialCapacity) {
+ if(initialCapacity < 0) {
+ throw new IllegalArgumentException("initial capacity must be 0 or higher");
+ }
+ keys = new long[initialCapacity];
+ values = new int[initialCapacity];
+ size = 0;
+ }
+
+ /**
+ * Increases size of array if needed
+ */
+ private void growIfNeeded() {
+ if (size == keys.length) {
+ // Resize.
+ long[] newKeysArray = new long[size * 3 / 2 + 10];
+ int[] newValuesArray = new int[size * 3 / 2 + 10];
+ System.arraycopy(keys, 0, newKeysArray, 0, size);
+ System.arraycopy(values, 0, newValuesArray, 0, size);
+ keys = newKeysArray;
+ values = newValuesArray;
+ }
+ }
+
+ /**
+ * Gets the long mapped from the specified key, or <code>0</code>
+ * if no such mapping has been made.
+ */
+ public int get(long key) {
+ return get(key, 0);
+ }
+
+ /**
+ * Gets the long mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public int get(long key, int valueIfKeyNotFound) {
+ int index = indexOfKey(key);
+ if(index >= 0) {
+ return values[index];
+ } else {
+ return valueIfKeyNotFound;
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public boolean delete(long key) {
+ int index = indexOfKey(key);
+
+ if (index >= 0) {
+ removeAt(index);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Removes the mapping at the given index.
+ */
+ public void removeAt(int index) {
+ System.arraycopy(keys, index + 1, keys, index, size - (index + 1));
+ System.arraycopy(values, index + 1, values, index, size - (index + 1));
+ size--;
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, int value) {
+ int index = indexOfKey(key);
+
+ if (index >= 0) {
+ values[index] = value;
+ } else {
+ growIfNeeded();
+ keys[size] = key;
+ values[size] = value;
+ size++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseIntArray
+ * currently stores.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseLongArray stores.
+ *
+ * <p>The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., <code>keyAt(0)</code> will return the
+ * smallest key and <code>keyAt(size()-1)</code> will return the largest
+ * key.</p>
+ */
+ public long keyAt(int index) {
+ if (index >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ } else if(index < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ return keys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseLongArray stores.
+ *
+ * <p>The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * <code>valueAt(0)</code> will return the value associated with the
+ * smallest key and <code>valueAt(size()-1)</code> will return the value
+ * associated with the largest key.</p>
+ */
+ public int valueAt(int index) {
+ if (index >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ } else if(index < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ return values[index];
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(long key) {
+ for(int i=0; i < size; i++) {
+ if(keys[i] == key) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(long value) {
+ for (int i = 0; i < size; i++) {
+ if (values[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseIntArray.
+ */
+ public void clear() {
+ keys = new long[10];
+ values = new int[10];
+ size = 0;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (! (other instanceof LongIntMap)) {
+ return false;
+ }
+ LongIntMap otherMap = (LongIntMap) other;
+ if (size != otherMap.size) {
+ return false;
+ }
+ for (int i = 0; i < size; i++) {
+ if (keys[i] != otherMap.keys[i] ||
+ values[i] != otherMap.values[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 1;
+ for (int i = 0; i < size; i++) {
+ long value = values[i];
+ hashCode = 31 * hashCode + (int)(value ^ (value >>> 32));
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "LongLongMap{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(size * 28);
+ buffer.append("LongLongMap{");
+ for (int i=0; i < size; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ long key = keyAt(i);
+ buffer.append(key);
+ buffer.append('=');
+ long value = valueAt(i);
+ buffer.append(value);
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
index f5d0cab0c..8934f3272 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
@@ -32,7 +32,6 @@ public final class LongList {
@Override
public int hashCode() {
- Arrays.hashCode(values);
int hashCode = 1;
for (int i = 0; i < size; i++) {
long value = values[i];
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 3904a1696..c6308fa7b 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -80,10 +80,10 @@
<string name="browse_gpoddernet_label">Browse gpodder.net</string>
<!-- Actions on feeds -->
- <string name="mark_all_read_label">Mark all as read</string>
- <string name="mark_all_read_msg">Marked all episodes as read</string>
- <string name="mark_all_read_confirmation_msg">Please confirm that you want to mark all episodes as being read.</string>
- <string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this feed as being read.</string>
+ <string name="mark_all_read_label">Mark all as played</string>
+ <string name="mark_all_read_msg">Marked all episodes as played</string>
+ <string name="mark_all_read_confirmation_msg">Please confirm that you want to mark all episodes as being played.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this feed as being played.</string>
<string name="show_info_label">Show information</string>
<string name="remove_feed_label">Remove podcast</string>
<string name="share_link_label">Share website link</string>
@@ -100,6 +100,7 @@
<string name="hide_downloaded_episodes_label">Downloaded</string>
<string name="hide_not_downloaded_episodes_label">Not downloaded</string>
<string name="filtered_label">Filtered</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Last refresh failed</string>
<!-- actions on feeditems -->
<string name="download_label">Download</string>
@@ -120,6 +121,9 @@
<string name="enqueue_all_new">Enqueue all</string>
<string name="download_all">Download all</string>
<string name="skip_episode_label">Skip episode</string>
+ <string name="activate_auto_download">Activate auto download</string>
+ <string name="deactivate_auto_download">Deactivate auto download</string>
+ <string name="reset_position">Reset playback position</string>
<!-- Download messages and labels -->
<string name="download_successful">successful</string>