AntennaPod/model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java

422 lines
11 KiB
Java

package de.danoeh.antennapod.model.feed;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Item (episode) within a feed.
*
* @author daniel
*/
public class FeedItem implements Serializable {
/** tag that indicates this item is in the queue */
public static final String TAG_QUEUE = "Queue";
/** tag that indicates this item is in favorites */
public static final String TAG_FAVORITE = "Favorite";
private long id;
/**
* The id/guid that can be found in the rss/atom feed. Might not be set.
*/
private String itemIdentifier;
private String title;
/**
* The description of a feeditem.
*/
private String description;
private String link;
private Date pubDate;
private FeedMedia media;
private transient Feed feed;
private long feedId;
private String podcastIndexChapterUrl;
private int state;
public static final int NEW = -1;
public static final int UNPLAYED = 0;
public static final int PLAYED = 1;
private String paymentLink;
/**
* Is true if the database contains any chapters that belong to this item. This attribute is only
* written once by DBReader on initialization.
* The FeedItem might still have a non-null chapters value. In this case, the list of chapters
* has not been saved in the database yet.
* */
private final boolean hasChapters;
/**
* The list of chapters of this item. This might be null even if there are chapters of this item
* in the database. The 'hasChapters' attribute should be used to check if this item has any chapters.
* */
private transient List<Chapter> chapters;
private String imageUrl;
private boolean autoDownloadEnabled = true;
/**
* Any tags assigned to this item
*/
private final Set<String> tags = new HashSet<>();
public FeedItem() {
this.state = UNPLAYED;
this.hasChapters = false;
}
/**
* This constructor is used by DBReader.
* */
public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
boolean hasChapters, String imageUrl, int state,
String itemIdentifier, boolean autoDownloadEnabled, String podcastIndexChapterUrl) {
this.id = id;
this.title = title;
this.link = link;
this.pubDate = pubDate;
this.paymentLink = paymentLink;
this.feedId = feedId;
this.hasChapters = hasChapters;
this.imageUrl = imageUrl;
this.state = state;
this.itemIdentifier = itemIdentifier;
this.autoDownloadEnabled = autoDownloadEnabled;
this.podcastIndexChapterUrl = podcastIndexChapterUrl;
}
/**
* This constructor should be used for creating test objects.
*/
public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed) {
this.id = id;
this.title = title;
this.itemIdentifier = itemIdentifier;
this.link = link;
this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
this.state = state;
this.feed = feed;
this.hasChapters = false;
}
/**
* This constructor should be used for creating test objects involving chapter marks.
*/
public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed, boolean hasChapters) {
this.id = id;
this.title = title;
this.itemIdentifier = itemIdentifier;
this.link = link;
this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
this.state = state;
this.feed = feed;
this.hasChapters = hasChapters;
}
public void updateFromOther(FeedItem other) {
if (other.imageUrl != null) {
this.imageUrl = other.imageUrl;
}
if (other.title != null) {
title = other.title;
}
if (other.getDescription() != null) {
description = other.getDescription();
}
if (other.link != null) {
link = other.link;
}
if (other.pubDate != null && !other.pubDate.equals(pubDate)) {
pubDate = other.pubDate;
}
if (other.media != null) {
if (media == null) {
setMedia(other.media);
// reset to new if feed item did link to a file before
setNew();
} else if (media.compareWithOther(other.media)) {
media.updateFromOther(other.media);
}
}
if (other.paymentLink != null) {
paymentLink = other.paymentLink;
}
if (other.chapters != null) {
if (!hasChapters) {
chapters = other.chapters;
}
}
if (other.podcastIndexChapterUrl != null) {
podcastIndexChapterUrl = other.podcastIndexChapterUrl;
}
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
/**
* Returns the value that uniquely identifies this FeedItem. If the
* itemIdentifier attribute is not null, it will be returned. Else it will
* try to return the title. If the title is not given, it will use the link
* of the entry.
*/
public String getIdentifyingValue() {
if (itemIdentifier != null && !itemIdentifier.isEmpty()) {
return itemIdentifier;
} else if (title != null && !title.isEmpty()) {
return title;
} else if (hasMedia() && media.getDownloadUrl() != null) {
return media.getDownloadUrl();
} else {
return link;
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public String getLink() {
return link;
}
/**
* Get the link for the feed item for the purpose of Share.
* It falls backs to the feed's link if the item has no link.
*/
public String getLinkWithFallback() {
if (StringUtils.isNotBlank(link)) {
return link;
} else if (StringUtils.isNotBlank(getFeed().getLink())) {
return getFeed().getLink();
}
return null;
}
public void setLink(String link) {
this.link = link;
}
public Date getPubDate() {
if (pubDate != null) {
return (Date) pubDate.clone();
} else {
return null;
}
}
public void setPubDate(Date pubDate) {
if (pubDate != null) {
this.pubDate = (Date) pubDate.clone();
} else {
this.pubDate = null;
}
}
@Nullable
public FeedMedia getMedia() {
return media;
}
/**
* Sets the media object of this FeedItem. If the given
* FeedMedia object is not null, it's 'item'-attribute value
* will also be set to this item.
*/
public void setMedia(FeedMedia media) {
this.media = media;
if (media != null && media.getItem() != this) {
media.setItem(this);
}
}
public Feed getFeed() {
return feed;
}
public void setFeed(Feed feed) {
this.feed = feed;
}
public boolean isNew() {
return state == NEW;
}
public int getPlayState() {
return state;
}
public void setNew() {
state = NEW;
}
public boolean isPlayed() {
return state == PLAYED;
}
public void setPlayed(boolean played) {
if (played) {
state = PLAYED;
} else {
state = UNPLAYED;
}
}
public boolean isInProgress() {
return (media != null && media.isInProgress());
}
/**
* Updates this item's description property if the given argument is longer than the already stored description
* @param newDescription The new item description, content:encoded, itunes:description, etc.
*/
public void setDescriptionIfLonger(String newDescription) {
if (newDescription == null) {
return;
}
if (this.description == null) {
this.description = newDescription;
} else if (this.description.length() < newDescription.length()) {
this.description = newDescription;
}
}
public String getPaymentLink() {
return paymentLink;
}
public void setPaymentLink(String paymentLink) {
this.paymentLink = paymentLink;
}
public List<Chapter> getChapters() {
return chapters;
}
public void setChapters(List<Chapter> chapters) {
this.chapters = chapters;
}
public String getItemIdentifier() {
return itemIdentifier;
}
public void setItemIdentifier(String itemIdentifier) {
this.itemIdentifier = itemIdentifier;
}
public boolean hasMedia() {
return media != null;
}
public String getImageLocation() {
if (imageUrl != null) {
return imageUrl;
} else if (media != null && media.hasEmbeddedPicture()) {
return FeedMedia.FILENAME_PREFIX_EMBEDDED_COVER + media.getLocalFileUrl();
} else if (feed != null) {
return feed.getImageUrl();
} else {
return null;
}
}
public long getFeedId() {
return feedId;
}
public void setFeedId(long feedId) {
this.feedId = feedId;
}
/**
* Returns the image of this item, as specified in the feed.
* To load the image that can be displayed to the user, use {@link #getImageLocation},
* which also considers embedded pictures or the feed picture if no other picture is present.
*/
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public boolean hasChapters() {
return hasChapters;
}
public void disableAutoDownload() {
this.autoDownloadEnabled = false;
}
public boolean isAutoDownloadEnabled() {
return this.autoDownloadEnabled;
}
public boolean isDownloaded() {
return media != null && media.isDownloaded();
}
/**
* @return true if the item has this tag
*/
public boolean isTagged(String tag) {
return tags.contains(tag);
}
/**
* @param tag adds this tag to the item. NOTE: does NOT persist to the database
*/
public void addTag(String tag) {
tags.add(tag);
}
/**
* @param tag the to remove
*/
public void removeTag(String tag) {
tags.remove(tag);
}
public String getPodcastIndexChapterUrl() {
return podcastIndexChapterUrl;
}
public void setPodcastIndexChapterUrl(String url) {
podcastIndexChapterUrl = url;
}
@NonNull
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}