diff options
Diffstat (limited to 'core/src/main/java/de')
23 files changed, 21 insertions, 1517 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java index a78ae71a3..a4b0436bd 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java @@ -35,7 +35,7 @@ import java.util.Arrays; import java.util.Collections; import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; public class OpmlBackupAgent extends BackupAgentHelper { private static final String OPML_BACKUP_KEY = "opml"; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java index 1a9654039..096a3757e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java @@ -30,7 +30,7 @@ import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.util.FastDocumentFile; import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat; import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.parser.feed.util.DateUtils; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java index 3eb69228e..ac1f739af 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateWorker.java @@ -24,7 +24,7 @@ import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.service.download.NewEpisodesNotification; import de.danoeh.antennapod.core.service.download.handler.FeedParserTask; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.net.common.NetworkUtils; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java index ed32957a4..4f9c7a190 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/EpisodeDownloadWorker.java @@ -21,7 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture; import de.danoeh.antennapod.core.ClientConfigurator; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.service.download.handler.MediaDownloadedHandler; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.event.MessageEvent; import de.danoeh.antennapod.model.download.DownloadError; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java index 0e017c612..62ce395f7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java @@ -16,7 +16,7 @@ import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.model.download.DownloadRequest; import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; import de.danoeh.antennapod.core.util.ChapterUtils; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index fc7ccd615..4e95a904d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -69,7 +69,7 @@ import de.danoeh.antennapod.storage.preferences.SleepTimerPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.service.QuickSettingsTileService; import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager.SleepTimer; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.FeedSearcher; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java index fad806770..49170c40f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java @@ -17,6 +17,7 @@ import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.database.DBReader; /** * Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod. diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java index 4f17760dc..cd24ca03d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java @@ -14,6 +14,7 @@ import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.database.DBReader; /** * A cleanup algorithm that removes any item that isn't in the queue and isn't a favorite diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java index 350a6fbbf..145693f2c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java @@ -13,6 +13,7 @@ import de.danoeh.antennapod.core.util.PlaybackStatus; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.net.common.NetworkUtils; import de.danoeh.antennapod.core.util.PowerUtils; 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 deleted file mode 100644 index 5e64fb1a1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ /dev/null @@ -1,860 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import android.database.Cursor; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.collection.ArrayMap; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import de.danoeh.antennapod.core.util.FeedItemPermutors; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.model.feed.Chapter; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedItemFilter; -import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.model.feed.FeedPreferences; -import de.danoeh.antennapod.model.feed.SortOrder; -import de.danoeh.antennapod.model.feed.SubscriptionsFilter; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.storage.database.PodDBAdapter; -import de.danoeh.antennapod.storage.database.mapper.ChapterCursorMapper; -import de.danoeh.antennapod.storage.database.mapper.DownloadResultCursorMapper; -import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper; -import de.danoeh.antennapod.storage.database.mapper.FeedItemCursorMapper; -import de.danoeh.antennapod.storage.database.mapper.FeedMediaCursorMapper; -import de.danoeh.antennapod.storage.database.mapper.FeedPreferencesCursorMapper; - -/** - * Provides methods for reading data from the AntennaPod database. - * In general, all database calls in DBReader-methods are executed on the caller's thread. - * This means that the caller should make sure that DBReader-methods are not executed on the GUI-thread. - */ -public final class DBReader { - - private static final String TAG = "DBReader"; - - /** - * Maximum size of the list returned by {@link #getDownloadLog()}. - */ - private static final int DOWNLOAD_LOG_SIZE = 200; - - - private DBReader() { - } - - /** - * Returns a list of Feeds, sorted alphabetically by their title. - * - * @return A list of Feeds, sorted alphabetically by their title. A Feed-object - * of the returned list does NOT have its list of FeedItems yet. The FeedItem-list - * can be loaded separately with {@link #getFeedItemList(Feed)}. - */ - @NonNull - public static List<Feed> getFeedList() { - Log.d(TAG, "Extracting Feedlist"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return getFeedList(adapter); - } finally { - adapter.close(); - } - } - - @NonNull - private static List<Feed> getFeedList(PodDBAdapter adapter) { - try (Cursor cursor = adapter.getAllFeedsCursor()) { - List<Feed> feeds = new ArrayList<>(cursor.getCount()); - while (cursor.moveToNext()) { - Feed feed = extractFeedFromCursorRow(cursor); - feeds.add(feed); - } - return feeds; - } - } - - /** - * Returns a list with the download URLs of all feeds. - * - * @return A list of Strings with the download URLs of all feeds. - */ - public static List<String> getFeedListDownloadUrls() { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getFeedCursorDownloadUrls()) { - List<String> result = new ArrayList<>(cursor.getCount()); - while (cursor.moveToNext()) { - String url = cursor.getString(1); - if (url != null && !url.startsWith(Feed.PREFIX_LOCAL_FOLDER)) { - result.add(url); - } - } - return result; - } finally { - adapter.close(); - } - } - - /** - * Loads additional data in to the feed items from other database queries - * - * @param items the FeedItems who should have other data loaded - */ - public static void loadAdditionalFeedItemListData(List<FeedItem> items) { - loadTagsOfFeedItemList(items); - loadFeedDataOfFeedItemList(items); - } - - private static void loadTagsOfFeedItemList(List<FeedItem> items) { - LongList favoriteIds = getFavoriteIDList(); - LongList queueIds = getQueueIDList(); - - for (FeedItem item : items) { - if (favoriteIds.contains(item.getId())) { - item.addTag(FeedItem.TAG_FAVORITE); - } - if (queueIds.contains(item.getId())) { - item.addTag(FeedItem.TAG_QUEUE); - } - } - } - - /** - * Takes a list of FeedItems and loads their corresponding Feed-objects from the database. - * The feedID-attribute of a FeedItem must be set to the ID of its feed or the method will - * not find the correct feed of an item. - * - * @param items The FeedItems whose Feed-objects should be loaded. - */ - private static void loadFeedDataOfFeedItemList(List<FeedItem> items) { - List<Feed> feeds = getFeedList(); - - Map<Long, Feed> feedIndex = new ArrayMap<>(feeds.size()); - for (Feed feed : feeds) { - feedIndex.put(feed.getId(), feed); - } - for (FeedItem item : items) { - Feed feed = feedIndex.get(item.getFeedId()); - if (feed == null) { - Log.w(TAG, "No match found for item with ID " + item.getId() + ". Feed ID was " + item.getFeedId()); - feed = new Feed("", "", "Error: Item without feed"); - } - item.setFeed(feed); - } - } - - /** - * Loads the list of FeedItems for a certain Feed-object. - * This method should NOT be used if the FeedItems are not used. - * - * @param feed The Feed whose items should be loaded - * @return A list with the FeedItems of the Feed. The Feed-attribute of the FeedItems will already be set correctly. - * The method does NOT change the items-attribute of the feed. - */ - public static List<FeedItem> getFeedItemList(final Feed feed) { - return getFeedItemList(feed, FeedItemFilter.unfiltered()); - } - - public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter) { - return getFeedItemList(feed, filter, SortOrder.DATE_NEW_OLD); - } - - public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder) { - Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getItemsOfFeedCursor(feed, filter)) { - List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - FeedItemPermutors.getPermutor(sortOrder).reorder(items); - feed.setItems(items); - for (FeedItem item : items) { - item.setFeed(feed); - } - return items; - } finally { - adapter.close(); - } - } - - public static List<FeedItem> extractItemlistFromCursor(Cursor itemlistCursor) { - Log.d(TAG, "extractItemlistFromCursor() called with: " + "itemlistCursor = [" + itemlistCursor + "]"); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return extractItemlistFromCursor(adapter, itemlistCursor); - } finally { - adapter.close(); - } - } - - @NonNull - private static List<FeedItem> extractItemlistFromCursor(PodDBAdapter adapter, Cursor cursor) { - List<FeedItem> result = new ArrayList<>(cursor.getCount()); - if (cursor.moveToFirst()) { - int indexMediaId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID); - do { - FeedItem item = FeedItemCursorMapper.convert(cursor); - result.add(item); - if (!cursor.isNull(indexMediaId)) { - item.setMedia(FeedMediaCursorMapper.convert(cursor)); - } - } while (cursor.moveToNext()); - } - return result; - } - - private static Feed extractFeedFromCursorRow(Cursor cursor) { - Feed feed = FeedCursorMapper.convert(cursor); - FeedPreferences preferences = FeedPreferencesCursorMapper.convert(cursor); - feed.setPreferences(preferences); - return feed; - } - - @NonNull - static List<FeedItem> getQueue(PodDBAdapter adapter) { - Log.d(TAG, "getQueue()"); - try (Cursor cursor = adapter.getQueueCursor()) { - List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - loadAdditionalFeedItemListData(items); - return items; - } - } - - /** - * Loads the IDs of the FeedItems in the queue. This method should be preferred over - * {@link #getQueue()} if the FeedItems of the queue are not needed. - * - * @return A list of IDs sorted by the same order as the queue. - */ - public static LongList getQueueIDList() { - Log.d(TAG, "getQueueIDList() called"); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return getQueueIDList(adapter); - } finally { - adapter.close(); - } - } - - private static LongList getQueueIDList(PodDBAdapter adapter) { - try (Cursor cursor = adapter.getQueueIDCursor()) { - LongList queueIds = new LongList(cursor.getCount()); - while (cursor.moveToNext()) { - queueIds.add(cursor.getLong(0)); - } - return queueIds; - } - } - - /** - * Loads a list of the FeedItems in the queue. If the FeedItems of the queue are not used directly, consider using - * {@link #getQueueIDList()} instead. - * - * @return A list of FeedItems sorted by the same order as the queue. - */ - @NonNull - public static List<FeedItem> getQueue() { - Log.d(TAG, "getQueue() called"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return getQueue(adapter); - } finally { - adapter.close(); - } - } - - private static LongList getFavoriteIDList() { - Log.d(TAG, "getFavoriteIDList() called"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getFavoritesIdsCursor(0, Integer.MAX_VALUE)) { - LongList favoriteIDs = new LongList(cursor.getCount()); - while (cursor.moveToNext()) { - favoriteIDs.add(cursor.getLong(0)); - } - return favoriteIDs; - } finally { - adapter.close(); - } - } - - /** - * - * @param offset The first episode that should be loaded. - * @param limit The maximum number of episodes that should be loaded. - * @param filter The filter describing which episodes to filter out. - */ - @NonNull - public static List<FeedItem> getEpisodes(int offset, int limit, FeedItemFilter filter, SortOrder sortOrder) { - Log.d(TAG, "getRecentlyPublishedEpisodes() called with: offset=" + offset + ", limit=" + limit); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getEpisodesCursor(offset, limit, filter, sortOrder)) { - List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - loadAdditionalFeedItemListData(items); - return items; - } finally { - adapter.close(); - } - } - - public static int getTotalEpisodeCount(FeedItemFilter filter) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getEpisodeCountCursor(filter)) { - if (cursor.moveToFirst()) { - return cursor.getInt(0); - } - return -1; - } finally { - adapter.close(); - } - } - - public static List<FeedItem> getRandomEpisodes(int limit, int seed) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getRandomEpisodesCursor(limit, seed)) { - List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - loadAdditionalFeedItemListData(items); - return items; - } finally { - adapter.close(); - } - } - - /** - * Loads the download log from the database. - * - * @return A list with DownloadStatus objects that represent the download log. - * The size of the returned list is limited by {@link #DOWNLOAD_LOG_SIZE}. - */ - public static List<DownloadResult> getDownloadLog() { - Log.d(TAG, "getDownloadLog() called"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getDownloadLogCursor(DOWNLOAD_LOG_SIZE)) { - List<DownloadResult> downloadLog = new ArrayList<>(cursor.getCount()); - while (cursor.moveToNext()) { - downloadLog.add(DownloadResultCursorMapper.convert(cursor)); - } - return downloadLog; - } finally { - adapter.close(); - } - } - - /** - * Loads the download log for a particular feed from the database. - * - * @param feedId Feed id for which the download log is loaded - * @return A list with DownloadStatus objects that represent the feed's download log, - * newest events first. - */ - public static List<DownloadResult> getFeedDownloadLog(long feedId) { - Log.d(TAG, "getFeedDownloadLog() called with: " + "feed = [" + feedId + "]"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getDownloadLog(Feed.FEEDFILETYPE_FEED, feedId)) { - List<DownloadResult> downloadLog = new ArrayList<>(cursor.getCount()); - while (cursor.moveToNext()) { - downloadLog.add(DownloadResultCursorMapper.convert(cursor)); - } - return downloadLog; - } finally { - adapter.close(); - } - } - - /** - * Loads a specific Feed from the database. - * - * @param feedId The ID of the Feed - * @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the - * database and the items-attribute will be set correctly. - */ - @Nullable - public static Feed getFeed(final long feedId) { - return getFeed(feedId, false); - } - - /** - * Loads a specific Feed from the database. - * - * @param feedId The ID of the Feed - * @param filtered <code>true</code> if only the visible items should be loaded according to the feed filter. - * @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the - * database and the items-attribute will be set correctly. - */ - @Nullable - public static Feed getFeed(final long feedId, boolean filtered) { - Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]"); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - Feed feed = null; - try (Cursor cursor = adapter.getFeedCursor(feedId)) { - if (cursor.moveToNext()) { - feed = extractFeedFromCursorRow(cursor); - if (filtered) { - feed.setItems(getFeedItemList(feed, feed.getItemFilter())); - } else { - feed.setItems(getFeedItemList(feed)); - } - } else { - Log.e(TAG, "getFeed could not find feed with id " + feedId); - } - return feed; - } finally { - adapter.close(); - } - } - - @Nullable - private static FeedItem getFeedItem(final long itemId, PodDBAdapter adapter) { - Log.d(TAG, "Loading feeditem with id " + itemId); - - FeedItem item = null; - try (Cursor cursor = adapter.getFeedItemCursor(Long.toString(itemId))) { - if (cursor.moveToNext()) { - List<FeedItem> list = extractItemlistFromCursor(adapter, cursor); - if (!list.isEmpty()) { - item = list.get(0); - loadAdditionalFeedItemListData(list); - } - } - return item; - } - } - - /** - * Loads a specific FeedItem from the database. This method should not be used for loading more - * than one FeedItem because this method might query the database several times for each item. - * - * @param itemId The ID of the FeedItem - * @return The FeedItem or null if the FeedItem could not be found. - */ - @Nullable - public static FeedItem getFeedItem(final long itemId) { - Log.d(TAG, "getFeedItem() called with: " + "itemId = [" + itemId + "]"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return getFeedItem(itemId, adapter); - } finally { - adapter.close(); - } - } - - /** - * Get next feed item in queue following a particular feeditem - * - * @param item The FeedItem - * @return The FeedItem next in queue or null if the FeedItem could not be found. - */ - @Nullable - public static FeedItem getNextInQueue(FeedItem item) { - Log.d(TAG, "getNextInQueue() called with: " + "itemId = [" + item.getId() + "]"); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - FeedItem nextItem = null; - try (Cursor cursor = adapter.getNextInQueue(item)) { - List<FeedItem> list = extractItemlistFromCursor(adapter, cursor); - if (!list.isEmpty()) { - nextItem = list.get(0); - loadAdditionalFeedItemListData(list); - } - return nextItem; - } catch (Exception e) { - return null; - } - } finally { - adapter.close(); - } - } - - @NonNull - public static List<FeedItem> getPausedQueue(int limit) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getPausedQueueCursor(limit)) { - List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); - loadAdditionalFeedItemListData(items); - return items; - } finally { - adapter.close(); - } - } - - /** - * Loads a specific FeedItem from the database. - * - * @param guid feed item guid - * @param episodeUrl the feed item's url - * @return The FeedItem or null if the FeedItem could not be found. - * Does NOT load additional attributes like feed or queue state. - */ - @Nullable - private static FeedItem getFeedItemByGuidOrEpisodeUrl(final String guid, final String episodeUrl, - PodDBAdapter adapter) { - try (Cursor cursor = adapter.getFeedItemCursor(guid, episodeUrl)) { - if (!cursor.moveToNext()) { - return null; - } - List<FeedItem> list = extractItemlistFromCursor(adapter, cursor); - if (!list.isEmpty()) { - return list.get(0); - } - return null; - } - } - - /** - * Loads a specific FeedItem from the database. - * - * @param guid feed item guid - * @param episodeUrl the feed item's url - * @return The FeedItem or null if the FeedItem could not be found. - * Does NOT load additional attributes like feed or queue state. - */ - public static FeedItem getFeedItemByGuidOrEpisodeUrl(final String guid, final String episodeUrl) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return getFeedItemByGuidOrEpisodeUrl(guid, episodeUrl, adapter); - } finally { - adapter.close(); - } - } - - /** - * Loads shownotes information about a FeedItem. - * - * @param item The FeedItem - */ - public static void loadDescriptionOfFeedItem(final FeedItem item) { - Log.d(TAG, "loadDescriptionOfFeedItem() called with: " + "item = [" + item + "]"); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getDescriptionOfItem(item)) { - if (cursor.moveToFirst()) { - int indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION); - String description = cursor.getString(indexDescription); - item.setDescriptionIfLonger(description); - } - } finally { - adapter.close(); - } - } - - /** - * Loads the list of chapters that belongs to this FeedItem if available. This method overwrites - * any chapters that this FeedItem has. If no chapters were found in the database, the chapters - * reference of the FeedItem will be set to null. - * - * @param item The FeedItem - */ - public static List<Chapter> loadChaptersOfFeedItem(final FeedItem item) { - Log.d(TAG, "loadChaptersOfFeedItem() called with: " + "item = [" + item + "]"); - - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try { - return loadChaptersOfFeedItem(adapter, item); - } finally { - adapter.close(); - } - } - - private static List<Chapter> loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) { - try (Cursor cursor = adapter.getSimpleChaptersOfFeedItemCursor(item)) { - int chaptersCount = cursor.getCount(); - if (chaptersCount == 0) { - item.setChapters(null); - return null; - } - ArrayList<Chapter> chapters = new ArrayList<>(); - while (cursor.moveToNext()) { - chapters.add(ChapterCursorMapper.convert(cursor)); - } - return chapters; - } - } - - /** - * Searches the DB for a FeedMedia of the given id. - * - * @param mediaId The id of the object - * @return The found object, or null if it does not exist - */ - @Nullable - public static FeedMedia getFeedMedia(final long mediaId) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - - try (Cursor mediaCursor = adapter.getSingleFeedMediaCursor(mediaId)) { - if (!mediaCursor.moveToFirst()) { - return null; - } - - int indexFeedItem = mediaCursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM); - long itemId = mediaCursor.getLong(indexFeedItem); - FeedMedia media = FeedMediaCursorMapper.convert(mediaCursor); - FeedItem item = getFeedItem(itemId); - if (item != null) { - media.setItem(item); - item.setMedia(media); - } - return media; - } finally { - adapter.close(); - } - } - - public static List<FeedItem> getFeedItemsWithUrl(List<String> urls) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor itemCursor = adapter.getFeedItemCursorByUrl(urls)) { - List<FeedItem> items = extractItemlistFromCursor(adapter, itemCursor); - loadAdditionalFeedItemListData(items); - return items; - } finally { - adapter.close(); - } - } - - public static class MonthlyStatisticsItem { - private int year = 0; - private int month = 0; - private long timePlayed = 0; - - public int getYear() { - return year; - } - - public void setYear(final int year) { - this.year = year; - } - - public int getMonth() { - return month; - } - - public void setMonth(final int month) { - this.month = month; - } - - public long getTimePlayed() { - return timePlayed; - } - - public void setTimePlayed(final long timePlayed) { - this.timePlayed = timePlayed; - } - } - - @NonNull - public static List<MonthlyStatisticsItem> getMonthlyTimeStatistics() { - List<MonthlyStatisticsItem> months = new ArrayList<>(); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getMonthlyStatisticsCursor()) { - int indexMonth = cursor.getColumnIndexOrThrow("month"); - int indexYear = cursor.getColumnIndexOrThrow("year"); - int indexTotalDuration = cursor.getColumnIndexOrThrow("total_duration"); - while (cursor.moveToNext()) { - MonthlyStatisticsItem item = new MonthlyStatisticsItem(); - item.setMonth(Integer.parseInt(cursor.getString(indexMonth))); - item.setYear(Integer.parseInt(cursor.getString(indexYear))); - item.setTimePlayed(cursor.getLong(indexTotalDuration)); - months.add(item); - } - } - adapter.close(); - return months; - } - - public static class StatisticsResult { - public List<StatisticsItem> feedTime = new ArrayList<>(); - public long oldestDate = System.currentTimeMillis(); - } - - /** - * Searches the DB for statistics. - * - * @return The list of statistics objects - */ - @NonNull - public static StatisticsResult getStatistics(boolean includeMarkedAsPlayed, - long timeFilterFrom, long timeFilterTo) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - - StatisticsResult result = new StatisticsResult(); - try (Cursor cursor = adapter.getFeedStatisticsCursor(includeMarkedAsPlayed, timeFilterFrom, timeFilterTo)) { - int indexOldestDate = cursor.getColumnIndexOrThrow("oldest_date"); - int indexNumEpisodes = cursor.getColumnIndexOrThrow("num_episodes"); - int indexEpisodesStarted = cursor.getColumnIndexOrThrow("episodes_started"); - int indexTotalTime = cursor.getColumnIndexOrThrow("total_time"); - int indexPlayedTime = cursor.getColumnIndexOrThrow("played_time"); - int indexNumDownloaded = cursor.getColumnIndexOrThrow("num_downloaded"); - int indexDownloadSize = cursor.getColumnIndexOrThrow("download_size"); - - while (cursor.moveToNext()) { - Feed feed = extractFeedFromCursorRow(cursor); - - long feedPlayedTime = Long.parseLong(cursor.getString(indexPlayedTime)) / 1000; - long feedTotalTime = Long.parseLong(cursor.getString(indexTotalTime)) / 1000; - long episodes = Long.parseLong(cursor.getString(indexNumEpisodes)); - long episodesStarted = Long.parseLong(cursor.getString(indexEpisodesStarted)); - long totalDownloadSize = Long.parseLong(cursor.getString(indexDownloadSize)); - long episodesDownloadCount = Long.parseLong(cursor.getString(indexNumDownloaded)); - long oldestDate = Long.parseLong(cursor.getString(indexOldestDate)); - - if (episodes > 0 && oldestDate < Long.MAX_VALUE) { - result.oldestDate = Math.min(result.oldestDate, oldestDate); - } - - result.feedTime.add(new StatisticsItem(feed, feedTotalTime, feedPlayedTime, episodes, - episodesStarted, totalDownloadSize, episodesDownloadCount)); - } - } - adapter.close(); - return result; - } - - public static long getTimeBetweenReleaseAndPlayback(long timeFilterFrom, long timeFilterTo) { - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - try (Cursor cursor = adapter.getTimeBetweenReleaseAndPlayback(timeFilterFrom, timeFilterTo)) { - cursor.moveToFirst(); - long result = Long.parseLong(cursor.getString(0)); - adapter.close(); - return result; - } - } - - /** - * Returns data necessary for displaying the navigation drawer. This includes - * the list of subscriptions, the number of items in the queue and the number of unread - * items. - */ - @NonNull - public static NavDrawerData getNavDrawerData(@Nullable SubscriptionsFilter subscriptionsFilter) { - Log.d(TAG, "getNavDrawerData() called with: " + ""); - PodDBAdapter adapter = PodDBAdapter.getInstance(); - adapter.open(); - - final Map<Long, Integer> feedCounters = adapter.getFeedCounters(UserPreferences.getFeedCounterSetting()); - List<Feed> feeds = getFeedList(adapter); - - if (subscriptionsFilter != null) { - feeds = subscriptionsFilter.filter(feeds, feedCounters); - } - - Comparator<Feed> comparator; - int feedOrder = UserPreferences.getFeedOrder(); - if (feedOrder == UserPreferences.FEED_ORDER_COUNTER) { - comparator = (lhs, rhs) -> { - long counterLhs = feedCounters.containsKey(lhs.getId()) ? feedCounters.get(lhs.getId()) : 0; - long counterRhs = feedCounters.containsKey(rhs.getId()) ? feedCounters.get(rhs.getId()) : 0; - if (counterLhs > counterRhs) { - // reverse natural order: podcast with most unplayed episodes first - return -1; - } else if (counterLhs == counterRhs) { - return lhs.getTitle().compareToIgnoreCase(rhs.getTitle()); - } else { - return 1; - } - }; - } else if (feedOrder == UserPreferences.FEED_ORDER_ALPHABETICAL) { - comparator = (lhs, rhs) -> { - String t1 = lhs.getTitle(); - String t2 = rhs.getTitle(); - if (t1 == null) { - return 1; - } else if (t2 == null) { - return -1; - } else { - return t1.compareToIgnoreCase(t2); - } - }; - } else if (feedOrder == UserPreferences.FEED_ORDER_MOST_PLAYED) { - final Map<Long, Integer> playedCounters = adapter.getPlayedEpisodesCounters(); - - comparator = (lhs, rhs) -> { - long counterLhs = playedCounters.containsKey(lhs.getId()) ? playedCounters.get(lhs.getId()) : 0; - long counterRhs = playedCounters.containsKey(rhs.getId()) ? playedCounters.get(rhs.getId()) : 0; - if (counterLhs > counterRhs) { - // podcast with most played episodes first - return -1; - } else if (counterLhs == counterRhs) { - return lhs.getTitle().compareToIgnoreCase(rhs.getTitle()); - } else { - return 1; - } - }; - } else { - final Map<Long, Long> recentPubDates = adapter.getMostRecentItemDates(); - comparator = (lhs, rhs) -> { - long dateLhs = recentPubDates.containsKey(lhs.getId()) ? recentPubDates.get(lhs.getId()) : 0; - long dateRhs = recentPubDates.containsKey(rhs.getId()) ? recentPubDates.get(rhs.getId()) : 0; - return Long.compare(dateRhs, dateLhs); - }; - } - - Collections.sort(feeds, comparator); - final int queueSize = adapter.getQueueSize(); - final int numNewItems = getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.NEW)); - final int numDownloadedItems = getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.DOWNLOADED)); - - List<NavDrawerData.DrawerItem> items = new ArrayList<>(); - Map<String, NavDrawerData.TagDrawerItem> folders = new HashMap<>(); - for (Feed feed : feeds) { - for (String tag : feed.getPreferences().getTags()) { - int counter = feedCounters.containsKey(feed.getId()) ? feedCounters.get(feed.getId()) : 0; - NavDrawerData.FeedDrawerItem drawerItem = new NavDrawerData.FeedDrawerItem(feed, feed.getId(), counter); - if (FeedPreferences.TAG_ROOT.equals(tag)) { - items.add(drawerItem); - continue; - } - NavDrawerData.TagDrawerItem folder; - if (folders.containsKey(tag)) { - folder = folders.get(tag); - } else { - folder = new NavDrawerData.TagDrawerItem(tag); - folders.put(tag, folder); - } - drawerItem.id |= folder.id; - folder.children.add(drawerItem); - } - } - List<NavDrawerData.TagDrawerItem> foldersSorted = new ArrayList<>(folders.values()); - Collections.sort(foldersSorted, (o1, o2) -> o1.getTitle().compareToIgnoreCase(o2.getTitle())); - items.addAll(foldersSorted); - - NavDrawerData result = new NavDrawerData(items, queueSize, numNewItems, numDownloadedItems, - feedCounters, EpisodeCleanupAlgorithmFactory.build().getReclaimableItems()); - adapter.close(); - return result; - } -} 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 883aefee8..b7112e6fd 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 @@ -18,6 +18,7 @@ import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.model.feed.FeedPreferences; import de.danoeh.antennapod.net.sync.model.EpisodeAction; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.database.PodDBAdapter; import de.danoeh.antennapod.storage.database.mapper.FeedCursorMapper; import de.danoeh.antennapod.storage.preferences.UserPreferences; 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 811dbfe5a..496eaf374 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 @@ -16,6 +16,7 @@ import de.danoeh.antennapod.event.DownloadLogEvent; import de.danoeh.antennapod.core.feed.LocalFeedUpdater; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.database.PodDBAdapter; import org.greenrobot.eventbus.EventBus; @@ -44,10 +45,10 @@ import de.danoeh.antennapod.storage.preferences.PlaybackPreferences; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.model.download.DownloadResult; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; -import de.danoeh.antennapod.core.util.FeedItemPermutors; +import de.danoeh.antennapod.storage.database.FeedItemPermutors; import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.core.util.Permutor; +import de.danoeh.antennapod.storage.database.LongList; +import de.danoeh.antennapod.storage.database.Permutor; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java index e6d3b2917..782abb4e7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import de.danoeh.antennapod.model.feed.FeedItemFilter; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.preferences.UserPreferences; public abstract class EpisodeCleanupAlgorithm { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java index 33c9e88a8..86c27ffdc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java @@ -15,6 +15,7 @@ import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.preferences.UserPreferences; /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java b/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java deleted file mode 100644 index 024c5d357..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java +++ /dev/null @@ -1,108 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import de.danoeh.antennapod.model.feed.Feed; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class NavDrawerData { - public final List<DrawerItem> items; - public final int queueSize; - public final int numNewItems; - public final int numDownloadedItems; - public final Map<Long, Integer> feedCounters; - public final int reclaimableSpace; - - public NavDrawerData(List<DrawerItem> feeds, - int queueSize, - int numNewItems, - int numDownloadedItems, - Map<Long, Integer> feedIndicatorValues, - int reclaimableSpace) { - this.items = feeds; - this.queueSize = queueSize; - this.numNewItems = numNewItems; - this.numDownloadedItems = numDownloadedItems; - this.feedCounters = feedIndicatorValues; - this.reclaimableSpace = reclaimableSpace; - } - - public abstract static class DrawerItem { - public enum Type { - TAG, FEED - } - - public final Type type; - private int layer; - public long id; - - public DrawerItem(Type type, long id) { - this.type = type; - this.id = id; - } - - public int getLayer() { - return layer; - } - - public void setLayer(int layer) { - this.layer = layer; - } - - public abstract String getTitle(); - - public abstract int getCounter(); - } - - public static class TagDrawerItem extends DrawerItem { - public final List<DrawerItem> children = new ArrayList<>(); - private final String name; - private boolean isOpen; - - public TagDrawerItem(String name) { - // Keep IDs >0 but make room for many feeds - super(DrawerItem.Type.TAG, Math.abs((long) name.hashCode()) << 20); - this.name = name; - } - - public String getTitle() { - return name; - } - - public boolean isOpen() { - return isOpen; - } - - public void setOpen(final boolean open) { - isOpen = open; - } - - public int getCounter() { - int sum = 0; - for (DrawerItem item : children) { - sum += item.getCounter(); - } - return sum; - } - } - - public static class FeedDrawerItem extends DrawerItem { - public final Feed feed; - public final int counter; - - public FeedDrawerItem(Feed feed, long id, int counter) { - super(DrawerItem.Type.FEED, id); - this.feed = feed; - this.counter = counter; - } - - public String getTitle() { - return feed.getTitle(); - } - - public int getCounter() { - return counter; - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java deleted file mode 100644 index 1bc4997dd..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import de.danoeh.antennapod.model.feed.Feed; - -public class StatisticsItem { - public final Feed feed; - public final long time; - - /** - * Respects speed, listening twice, ... - */ - public final long timePlayed; - - /** - * Number of episodes. - */ - public final long episodes; - - /** - * Episodes that are actually played. - */ - public final long episodesStarted; - - /** - * Simply sums up the size of download podcasts. - */ - public final long totalDownloadSize; - - /** - * Stores the number of episodes downloaded. - */ - public final long episodesDownloadCount; - - public StatisticsItem(Feed feed, long time, long timePlayed, - long episodes, long episodesStarted, - long totalDownloadSize, long episodesDownloadCount) { - this.feed = feed; - this.time = time; - this.timePlayed = timePlayed; - this.episodes = episodes; - this.episodesStarted = episodesStarted; - this.totalDownloadSize = totalDownloadSize; - this.episodesDownloadCount = episodesDownloadCount; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java index 5f750a50b..2dca8a9e7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java @@ -42,12 +42,12 @@ import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.event.SyncServiceEvent; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.net.common.AntennapodHttpClient; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueStorage; import de.danoeh.antennapod.core.util.FeedItemUtil; -import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.storage.database.LongList; import de.danoeh.antennapod.net.common.UrlChecker; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java index c058d47fe..1af81802b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java @@ -10,7 +10,7 @@ import de.danoeh.antennapod.model.feed.Chapter; import de.danoeh.antennapod.core.feed.ChapterMerger; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.net.common.AntennapodHttpClient; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.parser.feed.PodcastIndexChapterParser; import de.danoeh.antennapod.parser.media.id3.ChapterReader; import de.danoeh.antennapod.parser.media.id3.ID3ReaderException; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java deleted file mode 100644 index 4e39681f4..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java +++ /dev/null @@ -1,200 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.SortOrder; - -/** - * Provides method for sorting the a list of {@link FeedItem} according to rules. - */ -public class FeedItemPermutors { - - /** - * Returns a Permutor that sorts a list appropriate to the given sort order. - * - * @return Permutor that sorts a list appropriate to the given sort order. - */ - @NonNull - public static Permutor<FeedItem> getPermutor(@NonNull SortOrder sortOrder) { - - Comparator<FeedItem> comparator = null; - Permutor<FeedItem> permutor = null; - - switch (sortOrder) { - case EPISODE_TITLE_A_Z: - comparator = (f1, f2) -> itemTitle(f1).compareTo(itemTitle(f2)); - break; - case EPISODE_TITLE_Z_A: - comparator = (f1, f2) -> itemTitle(f2).compareTo(itemTitle(f1)); - break; - case DATE_OLD_NEW: - comparator = (f1, f2) -> pubDate(f1).compareTo(pubDate(f2)); - break; - case DATE_NEW_OLD: - comparator = (f1, f2) -> pubDate(f2).compareTo(pubDate(f1)); - break; - case DURATION_SHORT_LONG: - comparator = (f1, f2) -> Integer.compare(duration(f1), duration(f2)); - break; - case DURATION_LONG_SHORT: - comparator = (f1, f2) -> Integer.compare(duration(f2), duration(f1)); - break; - case EPISODE_FILENAME_A_Z: - comparator = (f1, f2) -> itemLink(f1).compareTo(itemLink(f2)); - break; - case EPISODE_FILENAME_Z_A: - comparator = (f1, f2) -> itemLink(f2).compareTo(itemLink(f1)); - break; - case FEED_TITLE_A_Z: - comparator = (f1, f2) -> feedTitle(f1).compareTo(feedTitle(f2)); - break; - case FEED_TITLE_Z_A: - comparator = (f1, f2) -> feedTitle(f2).compareTo(feedTitle(f1)); - break; - case RANDOM: - permutor = Collections::shuffle; - break; - case SMART_SHUFFLE_OLD_NEW: - permutor = (queue) -> smartShuffle(queue, true); - break; - case SMART_SHUFFLE_NEW_OLD: - permutor = (queue) -> smartShuffle(queue, false); - break; - case SIZE_SMALL_LARGE: - comparator = (f1, f2) -> Long.compare(size(f1), size(f2)); - break; - case SIZE_LARGE_SMALL: - comparator = (f1, f2) -> Long.compare(size(f2), size(f1)); - break; - case COMPLETION_DATE_NEW_OLD: - comparator = (f1, f2) -> f2.getMedia().getPlaybackCompletionDate() - .compareTo(f1.getMedia().getPlaybackCompletionDate()); - break; - default: - throw new IllegalArgumentException("Permutor not implemented"); - } - - if (comparator != null) { - final Comparator<FeedItem> comparator2 = comparator; - permutor = (queue) -> Collections.sort(queue, comparator2); - } - return permutor; - } - - // Null-safe accessors - - @NonNull - private static Date pubDate(@Nullable FeedItem item) { - return (item != null && item.getPubDate() != null) ? item.getPubDate() : new Date(0); - } - - @NonNull - private static String itemTitle(@Nullable FeedItem item) { - return (item != null && item.getTitle() != null) ? item.getTitle().toLowerCase(Locale.getDefault()) : ""; - } - - private static int duration(@Nullable FeedItem item) { - return (item != null && item.getMedia() != null) ? item.getMedia().getDuration() : 0; - } - - private static long size(@Nullable FeedItem item) { - return (item != null && item.getMedia() != null) ? item.getMedia().getSize() : 0; - } - - @NonNull - private static String itemLink(@Nullable FeedItem item) { - return (item != null && item.getLink() != null) - ? item.getLink().toLowerCase(Locale.getDefault()) : ""; - } - - @NonNull - private static String feedTitle(@Nullable FeedItem item) { - return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null) - ? item.getFeed().getTitle().toLowerCase(Locale.getDefault()) : ""; - } - - /** - * Implements a reordering by pubdate that avoids consecutive episodes from the same feed in - * the queue. - * - * A listener might want to hear episodes from any given feed in pubdate order, but would - * prefer a more balanced ordering that avoids having to listen to clusters of consecutive - * episodes from the same feed. This is what "Smart Shuffle" tries to accomplish. - * - * Assume the queue looks like this: `ABCDDEEEEEEEEEE`. - * This method first starts with a queue of the final size, where each slot is empty (null). - * It takes the podcast with most episodes (`E`) and places the episodes spread out in the queue: `EE_E_EE_E_EE_EE`. - * The podcast with the second-most number of episodes (`D`) is then - * placed spread-out in the *available* slots: `EE_EDEE_EDEE_EE`. - * This continues, until we end up with: `EEBEDEECEDEEAEE`. - * - * Note that episodes aren't strictly ordered in terms of pubdate, but episodes of each feed are. - * - * @param queue A (modifiable) list of FeedItem elements to be reordered. - * @param ascending {@code true} to use ascending pubdate in the reordering; - * {@code false} for descending. - */ - private static void smartShuffle(List<FeedItem> queue, boolean ascending) { - // Divide FeedItems into lists by feed - Map<Long, List<FeedItem>> map = new HashMap<>(); - for (FeedItem item : queue) { - Long id = item.getFeedId(); - if (!map.containsKey(id)) { - map.put(id, new ArrayList<>()); - } - map.get(id).add(item); - } - - // Sort each individual list by PubDate (ascending/descending) - Comparator<FeedItem> itemComparator = ascending - ? (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate()) - : (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate()); - List<List<FeedItem>> feeds = new ArrayList<>(); - for (Map.Entry<Long, List<FeedItem>> mapEntry : map.entrySet()) { - Collections.sort(mapEntry.getValue(), itemComparator); - feeds.add(mapEntry.getValue()); - } - - ArrayList<Integer> emptySlots = new ArrayList<>(); - for (int i = 0; i < queue.size(); i++) { - queue.set(i, null); - emptySlots.add(i); - } - - // Starting with the largest feed, place items spread out through the empty slots in the queue - Collections.sort(feeds, (f1, f2) -> Integer.compare(f2.size(), f1.size())); - for (List<FeedItem> feedItems : feeds) { - double spread = (double) emptySlots.size() / (feedItems.size() + 1); - Iterator<Integer> emptySlotIterator = emptySlots.iterator(); - int skipped = 0; - int placed = 0; - while (emptySlotIterator.hasNext()) { - int nextEmptySlot = emptySlotIterator.next(); - skipped++; - if (skipped >= spread * (placed + 1)) { - if (queue.get(nextEmptySlot) != null) { - throw new RuntimeException("Slot to be placed in not empty"); - } - queue.set(nextEmptySlot, feedItems.get(placed)); - emptySlotIterator.remove(); - placed++; - if (placed == feedItems.size()) { - break; - } - } - } - } - } -} 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 deleted file mode 100644 index 742920702..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java +++ /dev/null @@ -1,273 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import java.util.Arrays; - -/** - * Fast and memory efficient long list - */ -public final class LongList { - - private long[] values; - private int size; - - /** - * Constructs an empty instance with a default initial capacity. - */ - public LongList() { - this(4); - } - - /** - * Constructs an empty instance. - * - * @param initialCapacity {@code >= 0;} initial capacity of the list - */ - public LongList(int initialCapacity) { - if(initialCapacity < 0) { - throw new IllegalArgumentException("initial capacity must be 0 or higher"); - } - values = new long[initialCapacity]; - size = 0; - } - - public static LongList of(long... values) { - if(values == null || values.length == 0) { - return new LongList(0); - } - LongList result = new LongList(values.length); - for(long value : values) { - result.add(value); - } - return result; - } - - @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 boolean equals(Object other) { - if (other == this) { - return true; - } - if (! (other instanceof LongList)) { - return false; - } - LongList otherList = (LongList) 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() { - StringBuilder sb = new StringBuilder(size * 5 + 10); - sb.append("LongList{"); - 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 long 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 long set(int index, long value) { - if (index >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } else if(index < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - long 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(long 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(long 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 values from this list. - * - * @param values values to remove - */ - public void removeAll(long[] values) { - for(long value : values) { - remove(value); - } - } - - /** - * Removes values from this list. - * - * @param list List with values to remove - */ - public void removeAll(LongList list) { - for(long value : list.values) { - remove(value); - } - } - - /** - * 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. - long[] newArray = new long[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(long 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 long[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(long 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 long[] toArray() { - return Arrays.copyOf(values, size); - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java b/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java deleted file mode 100644 index 7d6e20ab1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import java.util.List; - -/** - * Interface for passing around list permutor method. This is used for cases where a simple comparator - * won't work (e.g. Random, Smart Shuffle, etc). - * - * @param <E> the type of elements in the list - */ -public interface Permutor<E> { - /** - * Reorders the specified list. - * @param queue A (modifiable) list of elements to be reordered - */ - void reorder(List<E> queue); -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java index 807c869f9..9ac35fdb7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java @@ -13,7 +13,7 @@ import android.util.Pair; import android.view.SurfaceHolder; import androidx.annotation.NonNull; import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; import de.danoeh.antennapod.model.feed.FeedMedia; diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java index ddca49988..508d61e02 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterWorker.java @@ -10,7 +10,7 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.storage.preferences.PlaybackPreferences; -import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.model.playback.Playable; import de.danoeh.antennapod.playback.base.PlayerStatus; |