Refactoring: Remove Cursor related code from Feed class (#4968)

This commit is contained in:
Herbert Reiter 2021-02-28 14:16:07 +01:00 committed by GitHub
parent 6e76dcea3d
commit 47a0336c3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 201 additions and 65 deletions

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.feed; package de.danoeh.antennapod.core.feed;
import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -9,11 +8,10 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.SortOrder; import de.danoeh.antennapod.core.util.SortOrder;
/** /**
* Data Object for a whole feed * Data Object for a whole feed.
* *
* @author daniel * @author daniel
*/ */
@ -24,9 +22,14 @@ public class Feed extends FeedFile {
public static final String TYPE_ATOM1 = "atom"; public static final String TYPE_ATOM1 = "atom";
public static final String PREFIX_LOCAL_FOLDER = "antennapod_local:"; public static final String PREFIX_LOCAL_FOLDER = "antennapod_local:";
/* title as defined by the feed */ /**
* title as defined by the feed.
*/
private String feedTitle; private String feedTitle;
/* custom title set by the user */
/**
* custom title set by the user.
*/
private String customTitle; private String customTitle;
/** /**
@ -40,25 +43,25 @@ public class Feed extends FeedFile {
private String description; private String description;
private String language; private String language;
/** /**
* Name of the author * Name of the author.
*/ */
private String author; private String author;
private String imageUrl; private String imageUrl;
private List<FeedItem> items; private List<FeedItem> items;
/** /**
* String that identifies the last update (adopted from Last-Modified or ETag header) * String that identifies the last update (adopted from Last-Modified or ETag header).
*/ */
private String lastUpdate; private String lastUpdate;
private String paymentLink; private String paymentLink;
/** /**
* Feed type, for example RSS 2 or Atom * Feed type, for example RSS 2 or Atom.
*/ */
private String type; private String type;
/** /**
* Feed preferences * Feed preferences.
*/ */
private FeedPreferences preferences; private FeedPreferences preferences;
@ -130,7 +133,7 @@ public class Feed extends FeedFile {
} }
/** /**
* This constructor is used for test purposes * This constructor is used for test purposes.
*/ */
public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink, public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl, String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
@ -173,56 +176,6 @@ public class Feed extends FeedFile {
preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password); preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password);
} }
public static Feed fromCursor(Cursor cursor) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
int indexLastUpdate = cursor.getColumnIndex(PodDBAdapter.KEY_LASTUPDATE);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexCustomTitle = cursor.getColumnIndex(PodDBAdapter.KEY_CUSTOM_TITLE);
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION);
int indexPaymentLink = cursor.getColumnIndex(PodDBAdapter.KEY_PAYMENT_LINK);
int indexAuthor = cursor.getColumnIndex(PodDBAdapter.KEY_AUTHOR);
int indexLanguage = cursor.getColumnIndex(PodDBAdapter.KEY_LANGUAGE);
int indexType = cursor.getColumnIndex(PodDBAdapter.KEY_TYPE);
int indexFeedIdentifier = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_IDENTIFIER);
int indexFileUrl = cursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL);
int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL);
int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED);
int indexIsPaged = cursor.getColumnIndex(PodDBAdapter.KEY_IS_PAGED);
int indexNextPageLink = cursor.getColumnIndex(PodDBAdapter.KEY_NEXT_PAGE_LINK);
int indexHide = cursor.getColumnIndex(PodDBAdapter.KEY_HIDE);
int indexSortOrder = cursor.getColumnIndex(PodDBAdapter.KEY_SORT_ORDER);
int indexLastUpdateFailed = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
int indexImageUrl = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
Feed feed = new Feed(
cursor.getLong(indexId),
cursor.getString(indexLastUpdate),
cursor.getString(indexTitle),
cursor.getString(indexCustomTitle),
cursor.getString(indexLink),
cursor.getString(indexDescription),
cursor.getString(indexPaymentLink),
cursor.getString(indexAuthor),
cursor.getString(indexLanguage),
cursor.getString(indexType),
cursor.getString(indexFeedIdentifier),
cursor.getString(indexImageUrl),
cursor.getString(indexFileUrl),
cursor.getString(indexDownloadUrl),
cursor.getInt(indexDownloaded) > 0,
cursor.getInt(indexIsPaged) > 0,
cursor.getString(indexNextPageLink),
cursor.getString(indexHide),
SortOrder.fromCodeString(cursor.getString(indexSortOrder)),
cursor.getInt(indexLastUpdateFailed) > 0
);
FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
feed.setPreferences(preferences);
return feed;
}
/** /**
* Returns the item at the specified index. * Returns the item at the specified index.
* *

View File

@ -23,6 +23,7 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter; import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.mapper.FeedCursorMapper;
import de.danoeh.antennapod.core.util.LongIntMap; import de.danoeh.antennapod.core.util.LongIntMap;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator; import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
@ -204,7 +205,7 @@ public final class DBReader {
} }
private static Feed extractFeedFromCursorRow(Cursor cursor) { private static Feed extractFeedFromCursorRow(Cursor cursor) {
Feed feed = Feed.fromCursor(cursor); Feed feed = FeedCursorMapper.convert(cursor);
FeedPreferences preferences = FeedPreferences.fromCursor(cursor); FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
feed.setPreferences(preferences); feed.setPreferences(preferences);
return feed; return feed;

View File

@ -19,6 +19,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater; import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.mapper.FeedCursorMapper;
import de.danoeh.antennapod.core.sync.SyncService; import de.danoeh.antennapod.core.sync.SyncService;
import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
@ -516,7 +517,7 @@ public final class DBTasks {
List<Feed> items = new ArrayList<>(); List<Feed> items = new ArrayList<>();
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
items.add(Feed.fromCursor(cursor)); items.add(FeedCursorMapper.convert(cursor));
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
setResult(items); setResult(items);

View File

@ -15,6 +15,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import de.danoeh.antennapod.core.storage.mapper.FeedItemFilterQuery; import de.danoeh.antennapod.core.storage.mapper.FeedItemFilterQuery;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -372,6 +373,7 @@ public class PodDBAdapter {
* For more information see * For more information see
* <a href="https://github.com/robolectric/robolectric/issues/1890">robolectric/robolectric#1890</a>.</p> * <a href="https://github.com/robolectric/robolectric/issues/1890">robolectric/robolectric#1890</a>.</p>
*/ */
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public static void tearDownTests() { public static void tearDownTests() {
getInstance().dbHelper.close(); getInstance().dbHelper.close();
instance = null; instance = null;
@ -1379,7 +1381,16 @@ public class PodDBAdapter {
} }
/** /**
* Called when a database corruption happens * Insert raw data to the database. *
* Call method only for unit tests.
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public void insertTestData(@NonNull String table, @NonNull ContentValues values) {
db.insert(table, null, values);
}
/**
* Called when a database corruption happens.
*/ */
public static class PodDbErrorHandler implements DatabaseErrorHandler { public static class PodDbErrorHandler implements DatabaseErrorHandler {
@Override @Override

View File

@ -0,0 +1,70 @@
package de.danoeh.antennapod.core.storage.mapper;
import android.database.Cursor;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.SortOrder;
/**
* Converts a {@link Cursor} to a {@link Feed} object.
*/
public abstract class FeedCursorMapper {
/**
* Create a {@link Feed} instance from a database row (cursor).
*/
@NonNull
public static Feed convert(@NonNull Cursor cursor) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
int indexLastUpdate = cursor.getColumnIndex(PodDBAdapter.KEY_LASTUPDATE);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexCustomTitle = cursor.getColumnIndex(PodDBAdapter.KEY_CUSTOM_TITLE);
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION);
int indexPaymentLink = cursor.getColumnIndex(PodDBAdapter.KEY_PAYMENT_LINK);
int indexAuthor = cursor.getColumnIndex(PodDBAdapter.KEY_AUTHOR);
int indexLanguage = cursor.getColumnIndex(PodDBAdapter.KEY_LANGUAGE);
int indexType = cursor.getColumnIndex(PodDBAdapter.KEY_TYPE);
int indexFeedIdentifier = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_IDENTIFIER);
int indexFileUrl = cursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL);
int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL);
int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED);
int indexIsPaged = cursor.getColumnIndex(PodDBAdapter.KEY_IS_PAGED);
int indexNextPageLink = cursor.getColumnIndex(PodDBAdapter.KEY_NEXT_PAGE_LINK);
int indexHide = cursor.getColumnIndex(PodDBAdapter.KEY_HIDE);
int indexSortOrder = cursor.getColumnIndex(PodDBAdapter.KEY_SORT_ORDER);
int indexLastUpdateFailed = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
int indexImageUrl = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
Feed feed = new Feed(
cursor.getLong(indexId),
cursor.getString(indexLastUpdate),
cursor.getString(indexTitle),
cursor.getString(indexCustomTitle),
cursor.getString(indexLink),
cursor.getString(indexDescription),
cursor.getString(indexPaymentLink),
cursor.getString(indexAuthor),
cursor.getString(indexLanguage),
cursor.getString(indexType),
cursor.getString(indexFeedIdentifier),
cursor.getString(indexImageUrl),
cursor.getString(indexFileUrl),
cursor.getString(indexDownloadUrl),
cursor.getInt(indexDownloaded) > 0,
cursor.getInt(indexIsPaged) > 0,
cursor.getString(indexNextPageLink),
cursor.getString(indexHide),
SortOrder.fromCodeString(cursor.getString(indexSortOrder)),
cursor.getInt(indexLastUpdateFailed) > 0
);
FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
feed.setPreferences(preferences);
return feed;
}
}

View File

@ -0,0 +1,100 @@
package de.danoeh.antennapod.core.storage.mapper;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class)
public class FeedCursorMapperTest {
private PodDBAdapter adapter;
@Before
public void setUp() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
PodDBAdapter.init(context);
adapter = PodDBAdapter.getInstance();
writeFeedToDatabase();
}
@After
public void tearDown() {
PodDBAdapter.tearDownTests();
}
@SuppressWarnings("ConstantConditions")
@Test
public void testFromCursor() {
try (Cursor cursor = adapter.getAllFeedsCursor()) {
cursor.moveToNext();
Feed feed = FeedCursorMapper.convert(cursor);
assertTrue(feed.getId() >= 0);
assertEquals("feed custom title", feed.getTitle());
assertEquals("feed custom title", feed.getCustomTitle());
assertEquals("feed link", feed.getLink());
assertEquals("feed description", feed.getDescription());
assertEquals("feed payment link", feed.getPaymentLink());
assertEquals("feed author", feed.getAuthor());
assertEquals("feed language", feed.getLanguage());
assertEquals("feed image url", feed.getImageUrl());
assertEquals("feed file url", feed.getFile_url());
assertEquals("feed download url", feed.getDownload_url());
assertTrue(feed.isDownloaded());
assertEquals("feed last update", feed.getLastUpdate());
assertEquals("feed type", feed.getType());
assertEquals("feed identifier", feed.getFeedIdentifier());
assertTrue(feed.isPaged());
assertEquals("feed next page link", feed.getNextPageLink());
assertTrue(feed.getItemFilter().showUnplayed);
assertEquals(1, feed.getSortOrder().code);
assertTrue(feed.hasLastUpdateFailed());
}
}
/**
* Insert test data to the database.
* Uses raw database insert instead of adapter.setCompleteFeed() to avoid testing the Feed class
* against itself.
*/
private void writeFeedToDatabase() {
ContentValues values = new ContentValues();
values.put(PodDBAdapter.KEY_TITLE, "feed title");
values.put(PodDBAdapter.KEY_CUSTOM_TITLE, "feed custom title");
values.put(PodDBAdapter.KEY_LINK, "feed link");
values.put(PodDBAdapter.KEY_DESCRIPTION, "feed description");
values.put(PodDBAdapter.KEY_PAYMENT_LINK, "feed payment link");
values.put(PodDBAdapter.KEY_AUTHOR, "feed author");
values.put(PodDBAdapter.KEY_LANGUAGE, "feed language");
values.put(PodDBAdapter.KEY_IMAGE_URL, "feed image url");
values.put(PodDBAdapter.KEY_FILE_URL, "feed file url");
values.put(PodDBAdapter.KEY_DOWNLOAD_URL, "feed download url");
values.put(PodDBAdapter.KEY_DOWNLOADED, true);
values.put(PodDBAdapter.KEY_LASTUPDATE, "feed last update");
values.put(PodDBAdapter.KEY_TYPE, "feed type");
values.put(PodDBAdapter.KEY_FEED_IDENTIFIER, "feed identifier");
values.put(PodDBAdapter.KEY_IS_PAGED, true);
values.put(PodDBAdapter.KEY_NEXT_PAGE_LINK, "feed next page link");
values.put(PodDBAdapter.KEY_HIDE, "unplayed");
values.put(PodDBAdapter.KEY_SORT_ORDER, "1");
values.put(PodDBAdapter.KEY_LAST_UPDATE_FAILED, true);
adapter.insertTestData(PodDBAdapter.TABLE_NAME_FEEDS, values);
}
}