Added support for images in itunes:image tags.

This commit is contained in:
daniel oeh 2014-03-19 20:27:12 +01:00
parent b9ab10253a
commit e8b9e49ee9
15 changed files with 315 additions and 118 deletions

View File

@ -48,4 +48,8 @@ public abstract class Chapter extends FeedComponent {
this.link = link; this.link = link;
} }
@Override
public String getHumanReadableIdentifier() {
return title;
}
} }

View File

@ -5,7 +5,7 @@ package de.danoeh.antennapod.feed;
* @author daniel * @author daniel
* *
*/ */
public class FeedComponent { public abstract class FeedComponent {
protected long id; protected long id;
@ -41,5 +41,10 @@ public class FeedComponent {
} }
/**
* Should return a non-null, human-readable String so that the item can be
* identified by the user. Can be title, download-url, etc.
*/
public abstract String getHumanReadableIdentifier();
} }

View File

@ -20,12 +20,6 @@ public abstract class FeedFile extends FeedComponent {
this(null, null, false); this(null, null, false);
} }
/**
* Should return a non-null, human-readable String so that the item can be
* identified by the user. Can be title, download-url, etc.
*/
public abstract String getHumanReadableIdentifier();
public abstract int getTypeAsInt(); public abstract int getTypeAsInt();
/** /**

View File

@ -16,7 +16,7 @@ public class FeedImage extends FeedFile implements
public static final int FEEDFILETYPE_FEEDIMAGE = 1; public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title; protected String title;
protected Feed feed; protected FeedComponent owner;
public FeedImage(String download_url, String title) { public FeedImage(String download_url, String title) {
super(null, download_url, false); super(null, download_url, false);
@ -33,8 +33,8 @@ public class FeedImage extends FeedFile implements
@Override @Override
public String getHumanReadableIdentifier() { public String getHumanReadableIdentifier() {
if (feed != null && feed.getTitle() != null) { if (owner != null && owner.getHumanReadableIdentifier() != null) {
return feed.getTitle(); return owner.getHumanReadableIdentifier();
} else { } else {
return download_url; return download_url;
} }
@ -57,12 +57,12 @@ public class FeedImage extends FeedFile implements
this.title = title; this.title = title;
} }
public Feed getFeed() { public FeedComponent getOwner() {
return feed; return owner;
} }
public void setFeed(Feed feed) { public void setOwner(FeedComponent owner) {
this.feed = feed; this.owner = owner;
} }
@Override @Override

View File

@ -1,17 +1,17 @@
package de.danoeh.antennapod.feed; package de.danoeh.antennapod.feed;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.asynctask.ImageLoader; import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider; import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.flattr.FlattrStatus; import de.danoeh.antennapod.util.flattr.FlattrStatus;
import de.danoeh.antennapod.util.flattr.FlattrThing; import de.danoeh.antennapod.util.flattr.FlattrThing;
import org.apache.commons.lang3.StringUtils;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
/** /**
* Data Object for a XML message * Data Object for a XML message
@ -46,6 +46,7 @@ public class FeedItem extends FeedComponent implements
private String paymentLink; private String paymentLink;
private FlattrStatus flattrStatus; private FlattrStatus flattrStatus;
private List<Chapter> chapters; private List<Chapter> chapters;
private FeedImage image;
public FeedItem() { public FeedItem() {
this.read = true; this.read = true;
@ -54,7 +55,7 @@ public class FeedItem extends FeedComponent implements
/** /**
* This constructor should be used for creating test objects. * This constructor should be used for creating test objects.
* */ */
public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed) { public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed) {
this.id = id; this.id = id;
this.title = title; this.title = title;
@ -98,6 +99,9 @@ public class FeedItem extends FeedComponent implements
chapters = other.chapters; chapters = other.chapters;
} }
} }
if (image == null) {
image = other.image;
}
} }
/** /**
@ -164,7 +168,7 @@ public class FeedItem extends FeedComponent implements
* Sets the media object of this FeedItem. If the given * Sets the media object of this FeedItem. If the given
* FeedMedia object is not null, it's 'item'-attribute value * FeedMedia object is not null, it's 'item'-attribute value
* will also be set to this item. * will also be set to this item.
* */ */
public void setMedia(FeedMedia media) { public void setMedia(FeedMedia media) {
this.media = media; this.media = media;
if (media != null && media.getItem() != this) { if (media != null && media.getItem() != this) {
@ -277,10 +281,11 @@ public class FeedItem extends FeedComponent implements
@Override @Override
public InputStream openImageInputStream() { public InputStream openImageInputStream() {
InputStream out = null; InputStream out = null;
if (hasMedia()) { if (hasItemImage()) {
out = image.openImageInputStream();
} else if (hasMedia()) {
out = media.openImageInputStream(); out = media.openImageInputStream();
} } else if (feed.getImage() != null) {
if (out == null && feed.getImage() != null) {
out = feed.getImage().openImageInputStream(); out = feed.getImage().openImageInputStream();
} }
return out; return out;
@ -289,10 +294,11 @@ public class FeedItem extends FeedComponent implements
@Override @Override
public InputStream reopenImageInputStream(InputStream input) { public InputStream reopenImageInputStream(InputStream input) {
InputStream out = null; InputStream out = null;
if (hasMedia()) { if (hasItemImage()) {
out = image.reopenImageInputStream(input);
} else if (hasMedia()) {
out = media.reopenImageInputStream(input); out = media.reopenImageInputStream(input);
} } else if (feed.getImage() != null) {
if (out == null && feed.getImage() != null) {
out = feed.getImage().reopenImageInputStream(input); out = feed.getImage().reopenImageInputStream(input);
} }
return out; return out;
@ -301,10 +307,11 @@ public class FeedItem extends FeedComponent implements
@Override @Override
public String getImageLoaderCacheKey() { public String getImageLoaderCacheKey() {
String out = null; String out = null;
if (hasMedia()) { if (hasItemImage()) {
out = image.getImageLoaderCacheKey();
} else if (hasMedia()) {
out = media.getImageLoaderCacheKey(); out = media.getImageLoaderCacheKey();
} } else if (feed.getImage() != null) {
if (out == null && feed.getImage() != null) {
out = feed.getImage().getImageLoaderCacheKey(); out = feed.getImage().getImageLoaderCacheKey();
} }
return out; return out;
@ -318,4 +325,28 @@ public class FeedItem extends FeedComponent implements
this.feedId = feedId; this.feedId = feedId;
} }
/**
* Returns the image of this item or the image of the feed if this item does
* not have its own image.
*/
public FeedImage getImage() {
return (hasItemImage()) ? image : feed.getImage();
}
public void setImage(FeedImage image) {
this.image = image;
image.setOwner(this);
}
/**
* Returns true if this FeedItem has its own image, false otherwise.
*/
public boolean hasItemImage() {
return image != null;
}
@Override
public String getHumanReadableIdentifier() {
return title;
}
} }

View File

@ -4,8 +4,6 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.preferences.PlaybackPreferences; import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.storage.DBReader;
@ -205,8 +203,8 @@ public class FeedMedia extends FeedFile implements Playable {
} }
public FeedImage getImage() { public FeedImage getImage() {
if (item != null && item.getFeed() != null) { if (item != null) {
return item.getFeed().getImage(); return (item.hasItemImage()) ? item.getImage() : item.getFeed().getImage();
} }
return null; return null;
} }
@ -385,8 +383,13 @@ public class FeedMedia extends FeedFile implements Playable {
@Override @Override
public InputStream openImageInputStream() { public InputStream openImageInputStream() {
InputStream out = new Playable.DefaultPlayableImageLoader(this) InputStream out;
if (item.hasItemImage()) {
out = item.openImageInputStream();
} else {
out = new Playable.DefaultPlayableImageLoader(this)
.openImageInputStream(); .openImageInputStream();
}
if (out == null) { if (out == null) {
if (item.getFeed().getImage() != null) { if (item.getFeed().getImage() != null) {
return item.getFeed().getImage().openImageInputStream(); return item.getFeed().getImage().openImageInputStream();
@ -397,8 +400,13 @@ public class FeedMedia extends FeedFile implements Playable {
@Override @Override
public String getImageLoaderCacheKey() { public String getImageLoaderCacheKey() {
String out = new Playable.DefaultPlayableImageLoader(this) String out;
if (item.hasItemImage()) {
out = item.getImageLoaderCacheKey();
} else {
out = new Playable.DefaultPlayableImageLoader(this)
.getImageLoaderCacheKey(); .getImageLoaderCacheKey();
}
if (out == null) { if (out == null) {
if (item.getFeed().getImage() != null) { if (item.getFeed().getImage() != null) {
return item.getFeed().getImage().getImageLoaderCacheKey(); return item.getFeed().getImage().getImageLoaderCacheKey();
@ -410,7 +418,7 @@ public class FeedMedia extends FeedFile implements Playable {
@Override @Override
public InputStream reopenImageInputStream(InputStream input) { public InputStream reopenImageInputStream(InputStream input) {
if (input instanceof FileInputStream) { if (input instanceof FileInputStream) {
return item.getFeed().getImage().reopenImageInputStream(input); return item.getImage().reopenImageInputStream(input);
} else { } else {
return new Playable.DefaultPlayableImageLoader(this) return new Playable.DefaultPlayableImageLoader(this)
.reopenImageInputStream(input); .reopenImageInputStream(input);

View File

@ -23,7 +23,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DownloadActivity; import de.danoeh.antennapod.activity.DownloadActivity;
import de.danoeh.antennapod.activity.DownloadAuthenticationActivity; import de.danoeh.antennapod.activity.DownloadAuthenticationActivity;
import de.danoeh.antennapod.activity.DownloadLogActivity; import de.danoeh.antennapod.activity.DownloadLogActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.feed.*; import de.danoeh.antennapod.feed.*;
import de.danoeh.antennapod.storage.*; import de.danoeh.antennapod.storage.*;
import de.danoeh.antennapod.syndication.handler.FeedHandler; import de.danoeh.antennapod.syndication.handler.FeedHandler;
@ -31,6 +30,7 @@ import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.util.ChapterUtils; import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.DownloadError; import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.InvalidFeedException; import de.danoeh.antennapod.util.InvalidFeedException;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -655,7 +655,7 @@ public class DownloadService extends Service {
&& savedFeed.getImage().isDownloaded() == false) { && savedFeed.getImage().isDownloaded() == false) {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Feed has image; Downloading...."); Log.d(TAG, "Feed has image; Downloading....");
savedFeed.getImage().setFeed(savedFeed); savedFeed.getImage().setOwner(savedFeed);
final Feed savedFeedRef = savedFeed; final Feed savedFeedRef = savedFeed;
try { try {
requester.downloadImage(DownloadService.this, requester.downloadImage(DownloadService.this,
@ -675,6 +675,32 @@ public class DownloadService extends Service {
); );
} }
} }
if (!hasDuplicateImages(savedFeed)) {
// download FeedItem images if provided and not downloaded
for (FeedItem item : savedFeed.getItems()) {
if (item.hasItemImage() && (!item.getImage().isDownloaded())) {
if (AppConfig.DEBUG)
Log.d(TAG, "Item has image; Downloading....");
try {
requester.downloadImage(DownloadService.this,
item.getImage());
} catch (DownloadRequestException e) {
e.printStackTrace();
DBWriter.addDownloadStatus(
DownloadService.this,
new DownloadStatus(
item.getImage(),
item
.getImage()
.getHumanReadableIdentifier(),
DownloadError.ERROR_REQUEST_ERROR,
false, e.getMessage()
)
);
}
}
}
}
} catch (SAXException e) { } catch (SAXException e) {
successful = false; successful = false;
@ -731,7 +757,25 @@ public class DownloadService extends Service {
if (AppConfig.DEBUG) if (AppConfig.DEBUG)
Log.d(TAG, "Feed appears to be valid."); Log.d(TAG, "Feed appears to be valid.");
return true; return true;
}
/**
* Checks if the FeedItems of this feed have images that point
* to the same URL.
*/
private boolean hasDuplicateImages(Feed feed) {
for (int x = 0; x < feed.getItems().size(); x++) {
for (int y = x + 1; y < feed.getItems().size(); y++) {
FeedItem item1 = feed.getItems().get(x);
FeedItem item2 = feed.getItems().get(y);
if (item1.hasItemImage() && item2.hasItemImage()) {
if (StringUtils.equals(item1.getImage().getDownload_url(), item2.getImage().getDownload_url())) {
return true;
}
}
}
}
return false;
} }
private boolean hasValidFeedItems(Feed feed) { private boolean hasValidFeedItems(Feed feed) {

View File

@ -5,6 +5,7 @@ import android.util.Log;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.util.DownloadError; import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.StorageUtils; import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.util.URIUtil; import de.danoeh.antennapod.util.URIUtil;
@ -32,6 +33,18 @@ public class HttpDownloader extends Downloader {
@Override @Override
protected void download() { protected void download() {
File destination = new File(request.getDestination());
if (destination.exists()) {
Log.w(TAG, "File already exists");
if (request.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE) {
onFail(DownloadError.ERROR_FILE_EXISTS, null);
return;
} else {
onSuccess();
return;
}
}
HttpClient httpClient = AntennapodHttpClient.getHttpClient(); HttpClient httpClient = AntennapodHttpClient.getHttpClient();
BufferedOutputStream out = null; BufferedOutputStream out = null;
InputStream connection = null; InputStream connection = null;
@ -79,13 +92,6 @@ public class HttpDownloader extends Downloader {
return; return;
} }
File destination = new File(request.getDestination());
if (destination.exists()) {
Log.w(TAG, "File already exists");
onFail(DownloadError.ERROR_FILE_EXISTS, null);
return;
}
connection = new BufferedInputStream(AndroidHttpClient connection = new BufferedInputStream(AndroidHttpClient
.getUngzippedContent(httpEntity)); .getUngzippedContent(httpEntity));
out = new BufferedOutputStream(new FileOutputStream( out = new BufferedOutputStream(new FileOutputStream(

View File

@ -217,6 +217,11 @@ public final class DBReader {
item.setFlattrStatus(new FlattrStatus(itemlistCursor item.setFlattrStatus(new FlattrStatus(itemlistCursor
.getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS))); .getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS)));
long imageIndex = itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_IMAGE);
if (imageIndex != 0) {
item.setImage(getFeedImage(adapter, imageIndex));
}
// extract chapters // extract chapters
boolean hasSimpleChapters = itemlistCursor boolean hasSimpleChapters = itemlistCursor
.getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0; .getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0;
@ -340,7 +345,7 @@ public final class DBReader {
new FlattrStatus(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_FLATTR_STATUS))); new FlattrStatus(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_FLATTR_STATUS)));
if (image != null) { if (image != null) {
image.setFeed(feed); image.setOwner(feed);
} }
FeedPreferences preferences = new FeedPreferences(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID), FeedPreferences preferences = new FeedPreferences(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID),
@ -740,7 +745,7 @@ public final class DBReader {
* @return The found object * @return The found object
*/ */
static FeedImage getFeedImage(PodDBAdapter adapter, final long id) { static FeedImage getFeedImage(PodDBAdapter adapter, final long id) {
Cursor cursor = adapter.getImageOfFeedCursor(id); Cursor cursor = adapter.getImageCursor(id);
if ((cursor.getCount() == 0) || !cursor.moveToFirst()) { if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
throw new SQLException("No FeedImage found at index: " + id); throw new SQLException("No FeedImage found at index: " + id);
} }

View File

@ -176,6 +176,16 @@ public class DBWriter {
&& requester.isDownloadingFile(item.getMedia())) { && requester.isDownloadingFile(item.getMedia())) {
requester.cancelDownload(context, item.getMedia()); requester.cancelDownload(context, item.getMedia());
} }
if (item.hasItemImage()) {
FeedImage image = item.getImage();
if (image.isDownloaded() && image.getFile_url() != null) {
File imgFile = new File(image.getFile_url());
imgFile.delete();
} else if (requester.isDownloadingFile(image)) {
requester.cancelDownload(context, item.getImage());
}
}
} }
PodDBAdapter adapter = new PodDBAdapter(context); PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open(); adapter.open();

View File

@ -157,7 +157,7 @@ public class DownloadRequester {
throws DownloadRequestException { throws DownloadRequestException {
if (feedFileValid(image)) { if (feedFileValid(image)) {
download(context, image, new File(getImagefilePath(context), download(context, image, new File(getImagefilePath(context),
getImagefileName(image)), true, null, null); getImagefileName(image)), false, null, null);
} }
} }
@ -291,8 +291,8 @@ public class DownloadRequester {
public String getImagefileName(FeedImage image) { public String getImagefileName(FeedImage image) {
String filename = image.getDownload_url(); String filename = image.getDownload_url();
if (image.getFeed() != null && image.getFeed().getTitle() != null) { if (image.getOwner() != null && image.getOwner().getHumanReadableIdentifier() != null) {
filename = image.getFeed().getTitle(); filename = image.getOwner().getHumanReadableIdentifier();
} }
return "image-" + FileNameGenerator.generateFileName(filename); return "image-" + FileNameGenerator.generateFileName(filename);
} }

View File

@ -168,7 +168,8 @@ public class PodDBAdapter {
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT," + KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER," + KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
+ KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT," + KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT,"
+ KEY_FLATTR_STATUS + " INTEGER)"; + KEY_FLATTR_STATUS + " INTEGER,"
+ KEY_IMAGE + " INTEGER)";
private static final String CREATE_TABLE_FEED_IMAGES = "CREATE TABLE " private static final String CREATE_TABLE_FEED_IMAGES = "CREATE TABLE "
+ TABLE_NAME_FEED_IMAGES + " (" + TABLE_PRIMARY_KEY + KEY_TITLE + TABLE_NAME_FEED_IMAGES + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@ -263,7 +264,8 @@ public class PodDBAdapter {
TABLE_NAME_FEED_ITEMS + "." + KEY_FEED, TABLE_NAME_FEED_ITEMS + "." + KEY_FEED,
TABLE_NAME_FEED_ITEMS + "." + KEY_HAS_CHAPTERS, TABLE_NAME_FEED_ITEMS + "." + KEY_HAS_CHAPTERS,
TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER, TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER,
TABLE_NAME_FEED_ITEMS + "." + KEY_FLATTR_STATUS}; TABLE_NAME_FEED_ITEMS + "." + KEY_FLATTR_STATUS,
TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE};
/** /**
* Contains FEEDITEM_SEL_FI_SMALL as comma-separated list. Useful for raw queries. * Contains FEEDITEM_SEL_FI_SMALL as comma-separated list. Useful for raw queries.
@ -288,6 +290,7 @@ public class PodDBAdapter {
public static final int IDX_FI_SMALL_HAS_CHAPTERS = 8; public static final int IDX_FI_SMALL_HAS_CHAPTERS = 8;
public static final int IDX_FI_SMALL_ITEM_IDENTIFIER = 9; public static final int IDX_FI_SMALL_ITEM_IDENTIFIER = 9;
public static final int IDX_FI_SMALL_FLATTR_STATUS = 10; public static final int IDX_FI_SMALL_FLATTR_STATUS = 10;
public static final int IDX_FI_SMALL_IMAGE = 11;
/** /**
* Select id, description and content-encoded column from feeditems. * Select id, description and content-encoded column from feeditems.
@ -416,10 +419,14 @@ public class PodDBAdapter {
db.update(TABLE_NAME_FEED_IMAGES, values, KEY_ID + "=?", db.update(TABLE_NAME_FEED_IMAGES, values, KEY_ID + "=?",
new String[]{String.valueOf(image.getId())}); new String[]{String.valueOf(image.getId())});
} }
if (image.getFeed() != null && image.getFeed().getId() != 0) {
final FeedComponent owner = image.getOwner();
if (owner != null && owner.getId() != 0) {
values.clear(); values.clear();
values.put(KEY_IMAGE, image.getId()); values.put(KEY_IMAGE, image.getId());
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(image.getFeed().getId())}); if (owner instanceof Feed) {
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(image.getOwner().getId())});
}
} }
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
@ -662,6 +669,13 @@ public class PodDBAdapter {
values.put(KEY_HAS_CHAPTERS, item.getChapters() != null); values.put(KEY_HAS_CHAPTERS, item.getChapters() != null);
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier()); values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong()); values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong());
if (item.hasItemImage()) {
if (item.getImage().getId() == 0) {
setImage(item.getImage());
}
values.put(KEY_IMAGE, item.getImage().getId());
}
if (item.getId() == 0) { if (item.getId() == 0) {
item.setId(db.insert(TABLE_NAME_FEED_ITEMS, null, values)); item.setId(db.insert(TABLE_NAME_FEED_ITEMS, null, values));
} else { } else {
@ -810,6 +824,9 @@ public class PodDBAdapter {
if (item.getChapters() != null) { if (item.getChapters() != null) {
removeChaptersOfItem(item); removeChaptersOfItem(item);
} }
if (item.hasItemImage()) {
removeFeedImage(item.getImage());
}
db.delete(TABLE_NAME_FEED_ITEMS, KEY_ID + "=?", db.delete(TABLE_NAME_FEED_ITEMS, KEY_ID + "=?",
new String[]{String.valueOf(item.getId())}); new String[]{String.valueOf(item.getId())});
} }
@ -914,7 +931,7 @@ public class PodDBAdapter {
* @param id ID of the FeedImage * @param id ID of the FeedImage
* @return The cursor of the query * @return The cursor of the query
*/ */
public final Cursor getImageOfFeedCursor(final long id) { public final Cursor getImageCursor(final long id) {
Cursor c = db.query(TABLE_NAME_FEED_IMAGES, null, KEY_ID + "=?", Cursor c = db.query(TABLE_NAME_FEED_IMAGES, null, KEY_ID + "=?",
new String[]{String.valueOf(id)}, null, null, null); new String[]{String.valueOf(id)}, null, null, null);
return c; return c;
@ -1337,6 +1354,9 @@ public class PodDBAdapter {
db.execSQL("ALTER TABLE " + TABLE_NAME_FEEDS db.execSQL("ALTER TABLE " + TABLE_NAME_FEEDS
+ " ADD COLUMN " + KEY_PASSWORD + " ADD COLUMN " + KEY_PASSWORD
+ " TEXT"); + " TEXT");
db.execSQL("ALTER TABLE" + TABLE_NAME_FEED_ITEMS
+ " ADD COLUMN " + KEY_IMAGE
+ " INTEGER");
} }
} }
} }

View File

@ -1,9 +1,8 @@
package de.danoeh.antennapod.syndication.namespace; package de.danoeh.antennapod.syndication.namespace;
import org.xml.sax.Attributes;
import de.danoeh.antennapod.feed.FeedImage; import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.syndication.handler.HandlerState; import de.danoeh.antennapod.syndication.handler.HandlerState;
import org.xml.sax.Attributes;
public class NSITunes extends Namespace { public class NSITunes extends Namespace {
public static final String NSTAG = "itunes"; public static final String NSTAG = "itunes";
@ -19,12 +18,24 @@ public class NSITunes extends Namespace{
@Override @Override
public SyndElement handleElementStart(String localName, HandlerState state, public SyndElement handleElementStart(String localName, HandlerState state,
Attributes attributes) { Attributes attributes) {
if (localName.equals(IMAGE) && state.getFeed().getImage() == null) { if (localName.equals(IMAGE)) {
FeedImage image = new FeedImage(); FeedImage image = new FeedImage();
image.setTitle(IMAGE_TITLE); image.setTitle(IMAGE_TITLE);
image.setDownload_url(attributes.getValue(IMAGE_HREF)); image.setDownload_url(attributes.getValue(IMAGE_HREF));
if (state.getCurrentItem() != null) {
// this is an items image
image.setTitle(state.getCurrentItem().getTitle() + IMAGE_TITLE);
state.getCurrentItem().setImage(image);
} else {
// this is the feed image
if (state.getFeed().getImage() == null) {
state.getFeed().setImage(image); state.getFeed().setImage(image);
} }
}
}
return new SyndElement(localName, this); return new SyndElement(localName, this);
} }

View File

@ -12,7 +12,8 @@ public class AntennaPodTestRunner extends InstrumentationTestRunner {
@Override @Override
public TestSuite getAllTests() { public TestSuite getAllTests() {
return new TestSuiteBuilder(AntennaPodTestRunner.class).includeAllPackagesUnderHere() return new TestSuiteBuilder(AntennaPodTestRunner.class).includePackages("instrumentationTest.de.test.antennapod.storage")
//.includeAllPackagesUnderHere()
// .excludePackages("instrumentationTest.de.test.antennapod.syndication.handler") // .excludePackages("instrumentationTest.de.test.antennapod.syndication.handler")
// .excludePackages("instrumentationTest.de.test.antennapod.gpodnet") // .excludePackages("instrumentationTest.de.test.antennapod.gpodnet")
.build(); .build();

View File

@ -95,7 +95,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File imgFile = new File(destFolder, "image"); File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile()); assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true); FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setFeed(feed); image.setOwner(feed);
feed.setImage(image); feed.setImage(image);
List<File> itemFiles = new ArrayList<File>(); List<File> itemFiles = new ArrayList<File>();
@ -137,7 +137,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageOfFeedCursor(image.getId()); c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
@ -217,7 +217,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File imgFile = new File(destFolder, "image"); File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile()); assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true); FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setFeed(feed); image.setOwner(feed);
feed.setImage(image); feed.setImage(image);
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext()); PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
@ -238,7 +238,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageOfFeedCursor(image.getId()); c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
} }
@ -254,7 +254,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File imgFile = new File(destFolder, "image"); File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile()); assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true); FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setFeed(feed); image.setOwner(feed);
feed.setImage(image); feed.setImage(image);
// create items // create items
@ -285,7 +285,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageOfFeedCursor(image.getId()); c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
@ -295,6 +295,64 @@ public class DBWriterTest extends InstrumentationTestCase {
} }
} }
public void testDeleteFeedWithItemImages() throws InterruptedException, ExecutionException, TimeoutException, IOException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
// create items with images
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
feed.getItems().add(item);
File itemImageFile = new File(destFolder, "item-image-" + i);
FeedImage itemImage = new FeedImage(0, "item-image" + i, itemImageFile.getAbsolutePath(), "url", true);
item.setImage(itemImage);
}
PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
assertTrue(item.getImage().getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
adapter = new PodDBAdapter(getInstrumentation().getContext());
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
c.close();
c = adapter.getImageCursor(item.getImage().getId());
assertEquals(0, c.getCount());
c.close();
}
}
public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException { public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER); File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder); assertNotNull(destFolder);
@ -305,7 +363,7 @@ public class DBWriterTest extends InstrumentationTestCase {
// create Feed image // create Feed image
File imgFile = new File(destFolder, "image"); File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true); FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setFeed(feed); image.setOwner(feed);
feed.setImage(image); feed.setImage(image);
List<File> itemFiles = new ArrayList<File>(); List<File> itemFiles = new ArrayList<File>();
@ -350,7 +408,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageOfFeedCursor(image.getId()); c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
@ -377,7 +435,7 @@ public class DBWriterTest extends InstrumentationTestCase {
// create Feed image // create Feed image
File imgFile = new File(destFolder, "image"); File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true); FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setFeed(feed); image.setOwner(feed);
feed.setImage(image); feed.setImage(image);
List<File> itemFiles = new ArrayList<File>(); List<File> itemFiles = new ArrayList<File>();
@ -412,7 +470,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageOfFeedCursor(image.getId()); c = adapter.getImageCursor(image.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {