package de.danoeh.antennapod.core.syndication.namespace.atom; import android.text.TextUtils; import android.util.Log; import org.xml.sax.Attributes; import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.syndication.handler.HandlerState; import de.danoeh.antennapod.core.syndication.namespace.NSITunes; import de.danoeh.antennapod.core.syndication.namespace.NSRSS20; import de.danoeh.antennapod.core.syndication.namespace.Namespace; import de.danoeh.antennapod.core.syndication.namespace.SyndElement; import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils; import de.danoeh.antennapod.core.util.DateUtils; public class NSAtom extends Namespace { private static final String TAG = "NSAtom"; public static final String NSTAG = "atom"; public static final String NSURI = "http://www.w3.org/2005/Atom"; private static final String FEED = "feed"; private static final String ID = "id"; private static final String TITLE = "title"; private static final String ENTRY = "entry"; private static final String LINK = "link"; private static final String UPDATED = "updated"; private static final String AUTHOR = "author"; private static final String CONTENT = "content"; private static final String IMAGE = "logo"; private static final String SUBTITLE = "subtitle"; private static final String PUBLISHED = "published"; private static final String TEXT_TYPE = "type"; // Link private static final String LINK_HREF = "href"; private static final String LINK_REL = "rel"; private static final String LINK_TYPE = "type"; private static final String LINK_TITLE = "title"; private static final String LINK_LENGTH = "length"; // rel-values private static final String LINK_REL_ALTERNATE = "alternate"; private static final String LINK_REL_ENCLOSURE = "enclosure"; private static final String LINK_REL_PAYMENT = "payment"; private static final String LINK_REL_RELATED = "related"; private static final String LINK_REL_SELF = "self"; private static final String LINK_REL_NEXT = "next"; // type-values private static final String LINK_TYPE_ATOM = "application/atom+xml"; private static final String LINK_TYPE_HTML = "text/html"; private static final String LINK_TYPE_XHTML = "application/xml+xhtml"; private static final String LINK_TYPE_RSS = "application/rss+xml"; /** * Regexp to test whether an Element is a Text Element. */ private static final String isText = TITLE + "|" + CONTENT + "|" + "|" + SUBTITLE; public static final String isFeed = FEED + "|" + NSRSS20.CHANNEL; public static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM; @Override public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) { if (ENTRY.equals(localName)) { state.setCurrentItem(new FeedItem()); state.getItems().add(state.getCurrentItem()); state.getCurrentItem().setFeed(state.getFeed()); } else if (localName.matches(isText)) { String type = attributes.getValue(TEXT_TYPE); return new AtomText(localName, this, type); } else if (LINK.equals(localName)) { String href = attributes.getValue(LINK_HREF); String rel = attributes.getValue(LINK_REL); SyndElement parent = state.getTagstack().peek(); if (parent.getName().matches(isFeedItem)) { if (LINK_REL_ALTERNATE.equals(rel)) { state.getCurrentItem().setLink(href); } else if (LINK_REL_ENCLOSURE.equals(rel)) { String strSize = attributes.getValue(LINK_LENGTH); long size = 0; try { if (strSize != null) { size = Long.parseLong(strSize); } } catch (NumberFormatException e) { Log.d(TAG, "Length attribute could not be parsed."); } String type = attributes.getValue(LINK_TYPE); boolean validType; if(SyndTypeUtils.enclosureTypeValid(type)) { validType = true; } else { type = SyndTypeUtils.getValidMimeTypeFromUrl(href); validType = type != null; } if (validType) { FeedItem currItem = state.getCurrentItem(); if(currItem != null && !currItem.hasMedia()) { currItem.setMedia(new FeedMedia(currItem, href, size, type)); } } } else if (LINK_REL_PAYMENT.equals(rel)) { state.getCurrentItem().setPaymentLink(href); } } else if (parent.getName().matches(isFeed)) { if (LINK_REL_ALTERNATE.equals(rel)) { String type = attributes.getValue(LINK_TYPE); /* * Use as link if a) no type-attribute is given and * feed-object has no link yet b) type of link is * LINK_TYPE_HTML or LINK_TYPE_XHTML */ if (state.getFeed() != null && ((type == null && state.getFeed().getLink() == null) || (LINK_TYPE_HTML.equals(type) || LINK_TYPE_XHTML.equals(type)))) { state.getFeed().setLink(href); } else if (LINK_TYPE_ATOM.equals(type) || LINK_TYPE_RSS.equals(type)) { // treat as podlove alternate feed String title = attributes.getValue(LINK_TITLE); if (TextUtils.isEmpty(title)) { title = href; } state.addAlternateFeedUrl(title, href); } } else if (LINK_REL_PAYMENT.equals(rel) && state.getFeed() != null) { state.getFeed().setPaymentLink(href); } else if (LINK_REL_NEXT.equals(rel) && state.getFeed() != null) { state.getFeed().setPaged(true); state.getFeed().setNextPageLink(href); } } } return new SyndElement(localName, this); } @Override public void handleElementEnd(String localName, HandlerState state) { if (ENTRY.equals(localName)) { if (state.getCurrentItem() != null && state.getTempObjects().containsKey(NSITunes.DURATION)) { FeedItem currentItem = state.getCurrentItem(); if (currentItem.hasMedia()) { Integer duration = (Integer) state.getTempObjects().get(NSITunes.DURATION); currentItem.getMedia().setDuration(duration); } state.getTempObjects().remove(NSITunes.DURATION); } state.setCurrentItem(null); } if (state.getTagstack().size() >= 2) { AtomText textElement = null; String content; if (state.getContentBuf() != null) { content = state.getContentBuf().toString(); } else { content = ""; } SyndElement topElement = state.getTagstack().peek(); String top = topElement.getName(); SyndElement secondElement = state.getSecondTag(); String second = secondElement.getName(); if (top.matches(isText)) { textElement = (AtomText) topElement; textElement.setContent(content); } if (ID.equals(top)) { if (FEED.equals(second) && state.getFeed() != null) { state.getFeed().setFeedIdentifier(content); } else if (ENTRY.equals(second) && state.getCurrentItem() != null) { state.getCurrentItem().setItemIdentifier(content); } } else if (TITLE.equals(top) && textElement != null) { if (FEED.equals(second) && state.getFeed() != null) { state.getFeed().setTitle(textElement.getProcessedContent()); } else if (ENTRY.equals(second) && state.getCurrentItem() != null) { state.getCurrentItem().setTitle(textElement.getProcessedContent()); } } else if (SUBTITLE.equals(top) && FEED.equals(second) && textElement != null && state.getFeed() != null) { state.getFeed().setDescription(textElement.getProcessedContent()); } else if (CONTENT.equals(top) && ENTRY.equals(second) && textElement != null && state.getCurrentItem() != null) { state.getCurrentItem().setDescription(textElement.getProcessedContent()); } else if (UPDATED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null && state.getCurrentItem().getPubDate() == null) { state.getCurrentItem().setPubDate(DateUtils.parse(content)); } else if (PUBLISHED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null) { state.getCurrentItem().setPubDate(DateUtils.parse(content)); } else if (IMAGE.equals(top) && state.getFeed() != null && state.getFeed().getImage() == null) { state.getFeed().setImage(new FeedImage(state.getFeed(), content, null)); } } } }