Let the database do the sorting (#7025)

This commit is contained in:
ByteHamster 2024-03-24 21:27:30 +01:00 committed by GitHub
parent 4078b3475e
commit 5c6000155c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 51 additions and 151 deletions

View File

@ -18,7 +18,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.actionbutton.DeleteActionButton;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.event.DownloadLogEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;

View File

@ -13,7 +13,7 @@ import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.event.DownloadLogEvent;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.databinding.DownloadLogFragmentBinding;

View File

@ -39,7 +39,7 @@ import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.event.FeedEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;

View File

@ -14,6 +14,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.event.playback.PlaybackHistoryEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@ -21,6 +22,7 @@ import java.util.List;
public class PlaybackHistoryFragment extends EpisodesListFragment {
public static final String TAG = "PlaybackHistoryFragment";
private static final FeedItemFilter FILTER_HISTORY = new FeedItemFilter(FeedItemFilter.IS_IN_HISTORY);
@NonNull
@Override
@ -90,17 +92,18 @@ public class PlaybackHistoryFragment extends EpisodesListFragment {
@NonNull
@Override
protected List<FeedItem> loadData() {
return DBReader.getPlaybackHistory(0, page * EPISODES_PER_PAGE);
return DBReader.getEpisodes(0, page * EPISODES_PER_PAGE, FILTER_HISTORY, SortOrder.COMPLETION_DATE_NEW_OLD);
}
@NonNull
@Override
protected List<FeedItem> loadMoreData(int page) {
return DBReader.getPlaybackHistory((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE);
return DBReader.getEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, FILTER_HISTORY,
SortOrder.COMPLETION_DATE_NEW_OLD);
}
@Override
protected int loadTotalItemCount() {
return (int) DBReader.getPlaybackHistoryLength();
return DBReader.getTotalEpisodeCount(FILTER_HISTORY);
}
}

View File

@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.event.DownloadLogEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.event.FeedItemEvent;

View File

@ -16,8 +16,6 @@ import java.util.Map;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadResultComparator;
import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparator;
import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
@ -341,57 +339,6 @@ public final class DBReader {
}
}
/**
* Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode
* has been completed at least once.
*
* @param limit The maximum number of items to return.
*
* @return The playback history. The FeedItems are sorted by their media's playbackCompletionDate in descending order.
*/
@NonNull
public static List<FeedItem> getPlaybackHistory(int offset, int limit) {
Log.d(TAG, "getPlaybackHistory() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor mediaCursor = null;
Cursor itemCursor = null;
try {
mediaCursor = adapter.getCompletedMediaCursor(offset, limit);
String[] itemIds = new String[mediaCursor.getCount()];
for (int i = 0; i < itemIds.length && mediaCursor.moveToPosition(i); i++) {
int index = mediaCursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM);
itemIds[i] = Long.toString(mediaCursor.getLong(index));
}
itemCursor = adapter.getFeedItemCursor(itemIds);
List<FeedItem> items = extractItemlistFromCursor(adapter, itemCursor);
loadAdditionalFeedItemListData(items);
Collections.sort(items, new PlaybackCompletionDateComparator());
return items;
} finally {
if (mediaCursor != null) {
mediaCursor.close();
}
if (itemCursor != null) {
itemCursor.close();
}
adapter.close();
}
}
public static long getPlaybackHistoryLength() {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
try {
return adapter.getCompletedMediaLength();
} finally {
adapter.close();
}
}
/**
* Loads the download log from the database.
*
@ -408,7 +355,6 @@ public final class DBReader {
while (cursor.moveToNext()) {
downloadLog.add(DownloadResultCursorMapper.convert(cursor));
}
Collections.sort(downloadLog, new DownloadResultComparator());
return downloadLog;
} finally {
adapter.close();
@ -432,7 +378,6 @@ public final class DBReader {
while (cursor.moveToNext()) {
downloadLog.add(DownloadResultCursorMapper.convert(cursor));
}
Collections.sort(downloadLog, new DownloadResultComparator());
return downloadLog;
} finally {
adapter.close();
@ -691,7 +636,6 @@ public final class DBReader {
try (Cursor itemCursor = adapter.getFeedItemCursorByUrl(urls)) {
List<FeedItem> items = extractItemlistFromCursor(adapter, itemCursor);
loadAdditionalFeedItemListData(items);
Collections.sort(items, new PlaybackCompletionDateComparator());
return items;
} finally {
adapter.close();

View File

@ -12,7 +12,7 @@ import androidx.documentfile.provider.DocumentFile;
import com.google.common.util.concurrent.Futures;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
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;
@ -39,7 +39,7 @@ import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.event.playback.PlaybackHistoryEvent;
import de.danoeh.antennapod.event.QueueEvent;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.event.FeedEvent;
import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.model.download.DownloadResult;

View File

@ -11,7 +11,6 @@ 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.core.util.comparator.ChapterStartTimeComparator;
import de.danoeh.antennapod.parser.feed.PodcastIndexChapterParser;
import de.danoeh.antennapod.parser.media.id3.ChapterReader;
import de.danoeh.antennapod.parser.media.id3.ID3ReaderException;
@ -30,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@ -232,4 +232,11 @@ public class ChapterUtils {
}
return true;
}
public static class ChapterStartTimeComparator implements Comparator<Chapter> {
@Override
public int compare(Chapter lhs, Chapter rhs) {
return Long.compare(lhs.getStart(), rhs.getStart());
}
}
}

View File

@ -78,6 +78,12 @@ public class FeedItemPermutors {
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) {

View File

@ -1,14 +0,0 @@
package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
import de.danoeh.antennapod.model.feed.Chapter;
public class ChapterStartTimeComparator implements Comparator<Chapter> {
@Override
public int compare(Chapter lhs, Chapter rhs) {
return Long.compare(lhs.getStart(), rhs.getStart());
}
}

View File

@ -1,14 +0,0 @@
package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
import de.danoeh.antennapod.model.download.DownloadResult;
/** Compares the completion date of two DownloadResult objects. */
public class DownloadResultComparator implements Comparator<DownloadResult> {
@Override
public int compare(DownloadResult lhs, DownloadResult rhs) {
return rhs.getCompletionDate().compareTo(lhs.getCompletionDate());
}
}

View File

@ -1,19 +0,0 @@
package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
import de.danoeh.antennapod.model.feed.FeedItem;
public class PlaybackCompletionDateComparator implements Comparator<FeedItem> {
public int compare(FeedItem lhs, FeedItem rhs) {
if (lhs.getMedia() != null
&& lhs.getMedia().getPlaybackCompletionDate() != null
&& rhs.getMedia() != null
&& rhs.getMedia().getPlaybackCompletionDate() != null) {
return rhs.getMedia().getPlaybackCompletionDate()
.compareTo(lhs.getMedia().getPlaybackCompletionDate());
}
return 0;
}
}

View File

@ -320,7 +320,7 @@ public class DbReaderTest {
}
adapter.close();
long len = DBReader.getPlaybackHistoryLength();
long len = DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.IS_IN_HISTORY));
assertEquals("Wrong size: ", (int) len, playedItems);
}
@ -500,7 +500,8 @@ public class DbReaderTest {
}
adapter.close();
List<FeedItem> saved = DBReader.getPlaybackHistory(paramOffset, paramLimit);
List<FeedItem> saved = DBReader.getEpisodes(paramOffset, paramLimit,
new FeedItemFilter(FeedItemFilter.IS_IN_HISTORY), SortOrder.COMPLETION_DATE_NEW_OLD);
assertNotNull(saved);
assertEquals(String.format("Wrong size with offset %d and limit %d: ",
paramOffset, paramLimit),

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.core.event;
package de.danoeh.antennapod.event;
import androidx.annotation.NonNull;

View File

@ -1,10 +1,7 @@
package de.danoeh.antennapod.core.feed;
package de.danoeh.antennapod.event;
import androidx.annotation.NonNull;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class FeedEvent {
public enum Action {
@ -23,10 +20,6 @@ public class FeedEvent {
@NonNull
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("action", action)
.append("feedId", feedId)
.toString();
return "FeedEvent{action=" + action + ", feedId=" + feedId + '}';
}
}

View File

@ -23,6 +23,7 @@ public class FeedItemFilter implements Serializable {
public final boolean showNoMedia;
public final boolean showIsFavorite;
public final boolean showNotFavorite;
public final boolean showInHistory;
public static final String PLAYED = "played";
public static final String UNPLAYED = "unplayed";
@ -37,6 +38,7 @@ public class FeedItemFilter implements Serializable {
public static final String NOT_QUEUED = "not_queued";
public static final String DOWNLOADED = "downloaded";
public static final String NOT_DOWNLOADED = "not_downloaded";
public static final String IS_IN_HISTORY = "is_in_history";
public static FeedItemFilter unfiltered() {
return new FeedItemFilter("");
@ -63,6 +65,7 @@ public class FeedItemFilter implements Serializable {
showIsFavorite = hasProperty(IS_FAVORITE);
showNotFavorite = hasProperty(NOT_FAVORITE);
showNew = hasProperty(NEW);
showInHistory = hasProperty(IS_IN_HISTORY);
}
private boolean hasProperty(String property) {
@ -106,6 +109,9 @@ public class FeedItemFilter implements Serializable {
return false;
} else if (showNotFavorite && item.isTagged(FeedItem.TAG_FAVORITE)) {
return false;
} else if (showInHistory && item.getMedia() != null
&& item.getMedia().getPlaybackCompletionDate().getTime() == 0) {
return false;
}
return true;
}

View File

@ -26,7 +26,8 @@ public enum SortOrder {
FEED_TITLE_Z_A(102, INTER_FEED),
RANDOM(103, INTER_FEED),
SMART_SHUFFLE_OLD_NEW(104, INTER_FEED),
SMART_SHUFFLE_NEW_OLD(105, INTER_FEED);
SMART_SHUFFLE_NEW_OLD(105, INTER_FEED),
COMPLETION_DATE_NEW_OLD(106, INTER_FEED);
public enum Scope {
INTRA_FEED, INTER_FEED

View File

@ -983,7 +983,7 @@ public class PodDBAdapter {
public final Cursor getDownloadLog(final int feedFileType, final long feedFileId) {
final String query = "SELECT * FROM " + TABLE_NAME_DOWNLOAD_LOG +
" WHERE " + KEY_FEEDFILE + "=" + feedFileId + " AND " + KEY_FEEDFILETYPE + "=" + feedFileType
+ " ORDER BY " + KEY_ID + " DESC";
+ " ORDER BY " + KEY_COMPLETION_DATE + " DESC";
return db.rawQuery(query, null);
}
@ -1107,29 +1107,6 @@ public class PodDBAdapter {
return "((" + SELECT_KEY_ITEM_ID + " * " + seed + ") % 46471)";
}
/**
* Returns a cursor which contains feed media objects with a playback
* completion date in ascending order.
*
* @param offset The row to start at.
* @param limit The maximum row count of the returned cursor. Must be an
* integer >= 0.
* @throws IllegalArgumentException if limit < 0
*/
public final Cursor getCompletedMediaCursor(int offset, int limit) {
if (limit < 0) {
throw new IllegalArgumentException("Limit must be >= 0");
}
return db.query(TABLE_NAME_FEED_MEDIA, null,
KEY_PLAYBACK_COMPLETION_DATE + " > 0", null, null,
null, String.format(Locale.US, "%s DESC LIMIT %d, %d", KEY_PLAYBACK_COMPLETION_DATE, offset, limit));
}
public final long getCompletedMediaLength() {
return DatabaseUtils.queryNumEntries(db, TABLE_NAME_FEED_MEDIA, KEY_PLAYBACK_COMPLETION_DATE + "> 0");
}
public final Cursor getSingleFeedMediaCursor(long id) {
final String query = "SELECT " + KEYS_FEED_MEDIA + " FROM " + TABLE_NAME_FEED_MEDIA
+ " WHERE " + KEY_ID + "=" + id;
@ -1168,7 +1145,8 @@ public class PodDBAdapter {
urlsString.append(DatabaseUtils.sqlEscapeString(urls.get(i)));
}
final String query = SELECT_FEED_ITEMS_AND_MEDIA
+ " WHERE " + KEY_DOWNLOAD_URL + " IN (" + urlsString + ")";
+ " WHERE " + KEY_DOWNLOAD_URL + " IN (" + urlsString + ")"
+ " ORDER BY " + KEY_PLAYBACK_COMPLETION_DATE + " DESC";
return db.rawQuery(query, null);
}

View File

@ -22,6 +22,7 @@ public class FeedItemFilterQuery {
// The keys used within this method, but explicitly combined with their table
String keyRead = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_READ;
String keyPosition = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_POSITION;
String keyCompletionDate = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE;
String keyDownloaded = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_DOWNLOADED;
String keyMediaId = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_ID;
String keyItemId = PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID;
@ -62,6 +63,9 @@ public class FeedItemFilterQuery {
} else if (filter.showNotFavorite) {
statements.add(keyItemId + " NOT IN (SELECT " + keyFeedItem + " FROM " + tableFavorites + ") ");
}
if (filter.showInHistory) {
statements.add(keyCompletionDate + " > 0 ");
}
if (statements.isEmpty()) {
return "";

View File

@ -31,6 +31,10 @@ public class FeedItemSortQuery {
case SIZE_LARGE_SMALL:
sortQuery = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_SIZE + " " + "DESC";
break;
case COMPLETION_DATE_NEW_OLD:
sortQuery = PodDBAdapter.TABLE_NAME_FEED_MEDIA + "."
+ PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE + " " + "DESC";
break;
default:
sortQuery = "";
break;