Browse Source

Added RSS feed feature UI.

pull/11/head
Eric Kok 12 years ago
parent
commit
e874e0eb71
  1. BIN
      core/libs/universal-image-loader-1.8.4.jar
  2. 16
      core/res/layout/activity_rssfeeds.xml
  3. 16
      core/res/layout/activity_rssitems.xml
  4. 28
      core/res/layout/fragment_rssfeeds.xml
  5. 28
      core/res/layout/fragment_rssitems.xml
  6. 46
      core/res/layout/list_item_rssfeed.xml
  7. 29
      core/res/layout/list_item_rssitem.xml
  8. 7
      core/res/menu/fragment_rssfeeds.xml
  9. 3
      core/res/values/strings.xml
  10. 149
      core/src/org/ifies/android/sax/Channel.java
  11. 150
      core/src/org/ifies/android/sax/HttpHelper.java
  12. 146
      core/src/org/ifies/android/sax/Item.java
  13. 237
      core/src/org/ifies/android/sax/RssParser.java
  14. 28
      core/src/org/transdroid/core/gui/navigation/NavigationHelper.java
  15. 63
      core/src/org/transdroid/core/gui/rss/RssfeedView.java
  16. 23
      core/src/org/transdroid/core/gui/rss/RssfeedsActivity.java
  17. 71
      core/src/org/transdroid/core/gui/rss/RssfeedsAdapter.java
  18. 43
      core/src/org/transdroid/core/gui/rss/RssfeedsFragment.java
  19. 63
      core/src/org/transdroid/core/gui/rss/RssitemStatusLayout.java
  20. 37
      core/src/org/transdroid/core/gui/rss/RssitemView.java
  21. 83
      core/src/org/transdroid/core/gui/rss/RssitemsAdapter.java
  22. 49
      core/src/org/transdroid/core/gui/rss/RssitemsFragment.java
  23. 6
      full/AndroidManifest.xml
  24. 3
      lib/src/org/transdroid/daemon/Synology/SynologyAdapter.java

BIN
core/libs/universal-image-loader-1.8.4.jar

Binary file not shown.

16
core/res/layout/activity_rssfeeds.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This layout is for phones in portrait and shows only the torrents list. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RssfeedsActivity" >
<fragment
android:id="@+id/rssfeeds_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="org.transdroid.core.gui.rss.RssFeedsFragment_"
tools:layout="@layout/fragment_rssfeeds" />
</FrameLayout>

16
core/res/layout/activity_rssitems.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This layout is for phones in portrait and shows only the torrents list. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RssItemsActivity" >
<fragment
android:id="@+id/rssfitems_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="org.transdroid.core.gui.rss.RssItemsFragment_"
tools:layout="@layout/fragment_rssitems" />
</FrameLayout>

28
core/res/layout/fragment_rssfeeds.xml

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.actionbarsherlock.view.SherlockListView
android:id="@+id/rssfeeds_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:listSelector="?attr/selectable_background_transdroid"
android:visibility="gone" />
<TextView
android:id="@+id/nosettings_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="8dip"
android:drawableTop="?attr/loading_progress"
android:gravity="center"
android:maxWidth="400dip"
android:padding="@dimen/margin_default"
android:text="@string/navigation_nosettings"
android:textIsSelectable="false"
android:visibility="gone" />
</FrameLayout>

28
core/res/layout/fragment_rssitems.xml

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.actionbarsherlock.view.SherlockListView
android:id="@+id/rssitems_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="multipleChoiceModal"
android:listSelector="?attr/selectable_background_transdroid"
android:visibility="gone" />
<TextView
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="8dip"
android:drawableTop="?attr/loading_progress"
android:gravity="center"
android:maxWidth="400dip"
android:padding="@dimen/margin_default"
android:text="@string/rss_empty"
android:textIsSelectable="false"
android:visibility="gone" />
</FrameLayout>

46
core/res/layout/list_item_rssfeed.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/margin_half"
android:paddingLeft="@dimen/margin_default"
android:paddingRight="@dimen/margin_default"
android:paddingTop="@dimen/margin_half" >
<ImageView
android:id="@+id/favicon_image"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_marginRight="@dimen/margin_half"
android:scaleType="centerCrop"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/name_text"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="1dip"
android:textIsSelectable="false" />
<TextView
android:id="@+id/newcount_text"
style="@style/LabelTextView"
android:layout_width="wrap_content"
android:layout_height="24dip"
android:minWidth="24dip"
android:paddingLeft="4dip"
android:gravity="center_horizontal"
android:textIsSelectable="false" />
<ProgressBar
android:id="@+id/loading_progress"
style="?android:attr/progressBarStyle"
android:layout_width="24dip"
android:layout_height="24dip"
android:indeterminate="true"
android:indeterminateOnly="true" />
</LinearLayout>

29
core/res/layout/list_item_rssitem.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<org.transdroid.core.gui.lists.RssitemStatusLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?attr/selectable_background_transdroid"
android:paddingBottom="@dimen/margin_half"
android:paddingLeft="@dimen/margin_default"
android:paddingRight="@dimen/margin_default"
android:paddingTop="@dimen/margin_half" >
<TextView
android:id="@+id/name_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-condensed"
android:textColor="?attr/text_bright"
android:textIsSelectable="true"
android:textSize="@dimen/text_enlarged" />
<TextView
android:id="@+id/date_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/name_text"
android:layout_marginTop="4dip"
android:textIsSelectable="false"
android:textSize="@dimen/text_small" />
</org.transdroid.core.gui.lists.RssitemStatusLayout>

7
core/res/menu/fragment_rssfeeds.xml

@ -0,0 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_settings"
android:title="@string/action_settings"/>
</menu>

3
core/res/values/strings.xml

@ -118,6 +118,9 @@
<string name="search_barcodescannernotfound">The Barcode Scanner could not be found. Would you like to install it from the Play Store?</string> <string name="search_barcodescannernotfound">The Barcode Scanner could not be found. Would you like to install it from the Play Store?</string>
<string name="search_filemanagernotfound">No compatible file manager could not be found. Would you like to install IO File Manager from the Play Store?</string> <string name="search_filemanagernotfound">No compatible file manager could not be found. Would you like to install IO File Manager from the Play Store?</string>
<string name="rss_nosettings">You have not defined any RSS feeds yet to monitor. Torrent-specific RSS feeds keep you up to date with new releases and you are notified of new items.</string>
<string name="rss_empty">The RSS feed is not available or it contains no items</string>
<string name="pref_servers">Servers</string> <string name="pref_servers">Servers</string>
<string name="pref_addserver">Add new server</string> <string name="pref_addserver">Add new server</string>
<string name="pref_searchsites">Search sites</string> <string name="pref_searchsites">Search sites</string>

149
core/src/org/ifies/android/sax/Channel.java

@ -0,0 +1,149 @@
/*
* Taken from the 'Learning Android' project,;
* released as Public Domain software at
* http://github.com/digitalspaghetti/learning-android
*/
package org.ifies.android.sax;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
public class Channel implements Parcelable {
public Channel() {
setCategories(new ArrayList<String>());
setItems(new ArrayList<Item>());
}
public void setId(int id) {
m_Id = id;
}
public int getId() {
return m_Id;
}
public void setTitle(String title) {
m_Title = title;
}
public String getTitle() {
return m_Title;
}
public void setLink(String link) {
m_Link = link;
}
public String getLink() {
return m_Link;
}
public void setDescription(String description) {
m_Description = description;
}
public String getDescription() {
return m_Description;
}
public void setPubDate(Date date) {
m_PubDate = date;
}
public Date getPubDate() {
return m_PubDate;
}
public void setLastBuildDate(long lastBuildDate) {
m_LastBuildDate = lastBuildDate;
}
public long getLastBuildDate() {
return m_LastBuildDate;
}
public void setCategories(List<String> categories) {
m_Categories = categories;
}
public void addCategory(String category) {
m_Categories.add(category);
}
public List<String> getCategories() {
return m_Categories;
}
public void setItems(List<Item> items) {
m_Items = items;
}
public void addItem(Item item) {
m_Items.add(item);
}
public List<Item> getItems() {
return m_Items;
}
public void setImage(String image) {
m_Image = image;
}
public String getImage() {
return m_Image;
}
private int m_Id;
private String m_Title;
private String m_Link;
private String m_Description;
private Date m_PubDate;
private long m_LastBuildDate;
private List<String> m_Categories;
private List<Item> m_Items;
private String m_Image;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(m_Id);
out.writeString(m_Title);
out.writeString(m_Link);
out.writeString(m_Description);
out.writeLong(m_PubDate == null? -1: m_PubDate.getTime());
out.writeLong(m_LastBuildDate);
out.writeStringList(m_Categories);
out.writeTypedList(m_Items);
out.writeString(m_Image);
}
public static final Parcelable.Creator<Channel> CREATOR = new Parcelable.Creator<Channel>() {
public Channel createFromParcel(Parcel in) {
return new Channel(in);
}
public Channel[] newArray(int size) {
return new Channel[size];
}
};
private Channel(Parcel in) {
m_Id = in.readInt();
m_Title = in.readString();
m_Link = in.readString();
m_Description = in.readString();
long pubDate = in.readLong();
m_PubDate = pubDate == -1? null: new Date(pubDate);
m_LastBuildDate = in.readLong();
m_Categories = new ArrayList<String>();
in.readTypedList(m_Items, Item.CREATOR);
in.readStringList(m_Categories);
m_Image = in.readString();
}
}

150
core/src/org/ifies/android/sax/HttpHelper.java

@ -0,0 +1,150 @@
/*
* This file is part of Transdroid Torrent Search
* <http://code.google.com/p/transdroid-search/>
*
* Transdroid Torrent Search is free software: you can redistribute
* it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later
* version.
*
* Transdroid Torrent Search is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ifies.android.sax;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.protocol.HttpContext;
/**
* Provides a set of general helper methods that can be used in web-based communication.
*
* @author erickok
*
*/
public class HttpHelper {
/**
* HTTP request interceptor to allow for GZip-encoded data transfer
*/
public static HttpRequestInterceptor gzipRequestInterceptor = new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
}
};
/**
* HTTP response interceptor that decodes GZipped data
*/
public static HttpResponseInterceptor gzipResponseInterceptor = new HttpResponseInterceptor() {
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
HttpEntity entity = response.getEntity();
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (int i = 0; i < codecs.length; i++) {
if (codecs[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(new HttpHelper.GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
};
/**
* HTTP entity wrapper to decompress GZipped HTTP responses
*/
private static class GzipDecompressingEntity extends HttpEntityWrapper {
public GzipDecompressingEntity(final HttpEntity entity) {
super(entity);
}
@Override
public InputStream getContent() throws IOException, IllegalStateException {
// the wrapped entity's getContent() decides about repeatability
InputStream wrappedin = wrappedEntity.getContent();
return new GZIPInputStream(wrappedin);
}
@Override
public long getContentLength() {
// length of ungzipped content is not known
return -1;
}
}
/*
* To convert the InputStream to String we use the BufferedReader.readLine()
* method. We iterate until the BufferedReader return null which means
* there's no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*
* Taken from http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple-restful-client-at-android/
*/
public static String ConvertStreamToString(InputStream is, String encoding) throws UnsupportedEncodingException {
InputStreamReader isr;
if (encoding != null) {
isr = new InputStreamReader(is, encoding);
} else {
isr = new InputStreamReader(is);
}
BufferedReader reader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
public static String ConvertStreamToString(InputStream is) {
try {
return ConvertStreamToString(is, null);
} catch (UnsupportedEncodingException e) {
// Since this is going to use the default encoding, it is never going to crash on an UnsupportedEncodingException
e.printStackTrace();
return null;
}
}
}

146
core/src/org/ifies/android/sax/Item.java

@ -0,0 +1,146 @@
/*
* Taken from the 'Learning Android' project,;
* released as Public Domain software at
* http://github.com/digitalspaghetti/learning-android
*/
package org.ifies.android.sax;
import java.util.Date;
import android.os.Parcel;
import android.os.Parcelable;
public class Item implements Comparable<Item>, Parcelable {
public void setId(int id) {
this._id = id;
}
public int getId() {
return _id;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setLink(String link) {
this.link = link;
}
public String getLink() {
return this.link;
}
public void setPubdate(Date pubdate) {
this.pubDate = pubdate;
}
public Date getPubdate() {
return this.pubDate;
}
public void setEnclosureUrl(String enclosureUrl) {
this.enclosureUrl = enclosureUrl;
}
public void setEnclosureLength(long enclosureLength) {
this.enclosureLength = enclosureLength;
}
public void setEnclosureType(String enclosureType) {
this.enclosureType = enclosureType;
}
public String getEnclosureUrl() {
return this.enclosureUrl;
}
public String getEnclosureType() {
return this.enclosureType;
}
public long getEnclosureLength() {
return this.enclosureLength;
}
private int _id;
private String title;
private String link;
private String description;
private Date pubDate;
private String enclosureUrl;
private String enclosureType;
private long enclosureLength;
/**
* Returns 'the' item link, which preferably is the enclosure url, but otherwise the link (or null if that is empty too)
* @return A single link url to be used
*/
public String getTheLink() {
if (this.getEnclosureUrl() != null) {
return this.getEnclosureUrl();
} else {
return this.getLink();
}
}
/**
* CompareTo is used to compare (and sort) item based on their publication dates
*/
@Override
public int compareTo(Item another) {
if (another == null || this.pubDate == null || another.getPubdate() == null) {
return 0;
}
return this.pubDate.compareTo(another.getPubdate());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(_id);
out.writeString(title);
out.writeString(link);
out.writeString(description);
out.writeLong(pubDate == null? -1: pubDate.getTime());
out.writeString(enclosureUrl);
out.writeString(enclosureType);
out.writeLong(enclosureLength);
}
public static final Parcelable.Creator<Item> CREATOR = new Parcelable.Creator<Item>() {
public Item createFromParcel(Parcel in) {
return new Item(in);
}
public Item[] newArray(int size) {
return new Item[size];
}
};
private Item(Parcel in) {
_id = in.readInt();
title = in.readString();
link = in.readString();
description = in.readString();
long pubDateIn = in.readLong();
pubDate = pubDateIn == -1? null: new Date(pubDateIn);
enclosureUrl = in.readString();
enclosureType = in.readString();
enclosureLength = in.readLong();
}
}

237
core/src/org/ifies/android/sax/RssParser.java

@ -0,0 +1,237 @@
/*
* Taken from the 'Learning Android' project,;
* released as Public Domain software at
* http://github.com/digitalspaghetti/learning-android
* and modified heavily for Transdroid
*/
package org.ifies.android.sax;
import java.io.IOException;
import java.util.Date;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class RssParser extends DefaultHandler
{
/**
* The constructor for the RSS Parser
* @param url
*/
public RssParser(String url) {
this.urlString = url;
this.text = new StringBuilder();
}
/**
* Returns the feed as a RssFeed, which is a ListArray
* @return RssFeed rssFeed
*/
public Channel getChannel() {
return (this.channel);
}
public void parse() throws ParserConfigurationException, SAXException, IOException {
DefaultHttpClient httpclient = initialise();
HttpResponse result = httpclient.execute(new HttpGet(urlString));
//FileInputStream urlInputStream = new FileInputStream("/sdcard/rsstest2.txt");
SAXParserFactory spf = SAXParserFactory.newInstance();
if (spf != null) {
SAXParser sp = spf.newSAXParser();
sp.parse(result.getEntity().getContent(), this);
}
}
/**
* Instantiates an HTTP client that can be used for all requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
protected DefaultHttpClient initialise() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", new PlainSocketFactory(), 80));
HttpParams httpparams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpparams, 5000);
HttpConnectionParams.setSoTimeout(httpparams, 5000);
DefaultHttpClient httpclient = new DefaultHttpClient(new ThreadSafeClientConnManager(httpparams, registry), httpparams);
httpclient.addRequestInterceptor(HttpHelper.gzipRequestInterceptor);
httpclient.addResponseInterceptor(HttpHelper.gzipResponseInterceptor);
return httpclient;
}
/**
* By default creates a standard Item (with title, description and links), which
* may to overriden to add more data.
* @return A possibly decorated Item instance
*/
protected Item createNewItem() {
return new Item();
}
public void startElement(String uri, String localName, String qName, Attributes attributes) {
/** First lets check for the channel */
if (localName.equalsIgnoreCase("channel")) {
this.channel = new Channel();
}
/** Now lets check for an item */
if (localName.equalsIgnoreCase("item") && (this.channel != null)) {
this.item = createNewItem();
this.channel.addItem(this.item);
}
/** Now lets check for an image */
if (localName.equalsIgnoreCase("image") && (this.channel != null)) {
this.imgStatus = true;
}
/** Checking for a enclosure */
if (localName.equalsIgnoreCase("enclosure")) {
/** Lets check we are in an item */
if (this.item != null && attributes != null && attributes.getLength() > 0) {
if (attributes.getValue("url") != null) {
this.item.setEnclosureUrl(parseLink(attributes.getValue("url")));
}
if (attributes.getValue("type") != null) {
this.item.setEnclosureType(attributes.getValue("type"));
}
if (attributes.getValue("length") != null) {
this.item.setEnclosureLength(Long.parseLong(attributes.getValue("length")));
}
}
}
}
/**
* This is where we actually parse for the elements contents
*/
@SuppressWarnings("deprecation")
public void endElement(String uri, String localName, String qName) {
/** Check we have an RSS Feed */
if (this.channel == null) {
return;
}
/** Check are at the end of an item */
if (localName.equalsIgnoreCase("item")) {
this.item = null;
}
/** Check we are at the end of an image */
if (localName.equalsIgnoreCase("image"))
this.imgStatus = false;
/** Now we need to parse which title we are in */
if (localName.equalsIgnoreCase("title"))
{
/** We are an item, so we set the item title */
if (this.item != null){
this.item.setTitle(this.text.toString().trim());
/** We are in an image */
} else {
this.channel.setTitle(this.text.toString().trim());
}
}
/** Now we are checking for a link */
if (localName.equalsIgnoreCase("link")) {
/** Check we are in an item **/
if (this.item != null) {
this.item.setLink(parseLink(this.text.toString()));
/** Check we are in an image */
} else if (this.imgStatus) {
this.channel.setImage(parseLink(this.text.toString()));
/** Check we are in a channel */
} else {
this.channel.setLink(parseLink(this.text.toString()));
}
}
/** Checking for a description */
if (localName.equalsIgnoreCase("description")) {
/** Lets check we are in an item */
if (this.item != null) {
this.item.setDescription(this.text.toString().trim());
/** Lets check we are in the channel */
} else {
this.channel.setDescription(this.text.toString().trim());
}
}
/** Checking for a pubdate */
if (localName.equalsIgnoreCase("pubDate")) {
/** Lets check we are in an item */
if (this.item != null) {
try {
this.item.setPubdate(new Date(Date.parse(this.text.toString().trim())));
} catch (Exception e) {
// Date is malformed (not parsable by Date.parse)
}
/** Lets check we are in the channel */
} else {
try {
this.channel.setPubDate(new Date(Date.parse(this.text.toString().trim())));
} catch (Exception e) {
// Date is malformed (not parsable by Date.parse)
}
}
}
/** Check for the category */
if (localName.equalsIgnoreCase("category") && (this.item != null)) {
this.channel.addCategory(this.text.toString().trim());
}
addAdditionalData(localName, this.item, this.text.toString());
this.text.setLength(0);
}
/**
* May be overridden to add additional data from tags that are not standard in RSS.
* Not used by this default RSS style parser.
* @param localName The tag name
* @param item The Item we are currently parsing
* @param text The new text content
*/
protected void addAdditionalData(String localName, Item item, String text) { }
public void characters(char[] ch, int start, int length) {
this.text.append(ch, start, length);
}
private String parseLink(String string) {
return string.trim();
}
private String urlString;
private Channel channel;
private StringBuilder text;
private Item item;
private boolean imgStatus;
}

28
core/src/org/transdroid/core/gui/navigation/NavigationHelper.java

@ -11,6 +11,15 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import com.nostra13.universalimageloader.cache.disc.impl.FileCountLimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.UsingFreqLimitedMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import de.keyboardsurfer.android.widget.crouton.Crouton; import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style; import de.keyboardsurfer.android.widget.crouton.Style;
@ -25,6 +34,7 @@ public class NavigationHelper {
@RootContext @RootContext
protected Context context; protected Context context;
private static ImageLoader imageCache;
/** /**
* Use with {@link Crouton#showText(android.app.Activity, int, Style)} (and variants) to display error messages. * Use with {@link Crouton#showText(android.app.Activity, int, Style)} (and variants) to display error messages.
@ -38,6 +48,24 @@ public class NavigationHelper {
public static Style CROUTON_INFO_STYLE = new Style.Builder().setBackgroundColor(R.color.crouton_info) public static Style CROUTON_INFO_STYLE = new Style.Builder().setBackgroundColor(R.color.crouton_info)
.setTextSize(13).setDuration(1500).build(); .setTextSize(13).setDuration(1500).build();
/**
* Returns (and initialises, if needed) an image cache that uses memory and (1MB) local storage.
* @return An image cache that loads web images synchronously and transparently
*/
public ImageLoader getImageCache() {
if (imageCache == null) {
imageCache = ImageLoader.getInstance();
Builder imageCacheBuilder = new Builder(context).defaultDisplayImageOptions(
new DisplayImageOptions.Builder().cacheInMemory().cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build()).memoryCache(
new UsingFreqLimitedMemoryCache(1024 * 1024));
imageCacheBuilder.discCache(new FileCountLimitedDiscCache(context.getCacheDir(),
new Md5FileNameGenerator(), 25));
imageCache.init(imageCacheBuilder.build());
}
return imageCache;
}
/** /**
* Whether any search-related UI components should be shown in the interface. At the moment returns false only if we * Whether any search-related UI components should be shown in the interface. At the moment returns false only if we
* run as Transdroid Lite version. * run as Transdroid Lite version.

63
core/src/org/transdroid/core/gui/rss/RssfeedView.java

@ -0,0 +1,63 @@
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.Background;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.navigation.NavigationHelper;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's
* site and can load how many new items are available.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_rssfeed")
public class RssfeedView extends LinearLayout {
private static final String GETFVO_URL = "http://g.etfv.co/%1$s";
@Bean
protected NavigationHelper navigationHelper;
// Views
@ViewById
protected ImageView faviconImage;
@ViewById
protected TextView nameText, newcountText;
@ViewById
protected ProgressBar loadingProgress;
public RssfeedView(Context context) {
super(context);
}
public void bind(RssfeedSetting rssfeed) {
nameText.setText(rssfeed.getName());
faviconImage.setImageDrawable(null);
loadingProgress.setVisibility(View.VISIBLE);
newcountText.setVisibility(View.VISIBLE);
// Load the RSS feed site' favicon
// Uses the g.etfv.co service to resolve the favicon of any feed URL
navigationHelper.getImageCache().displayImage(String.format(GETFVO_URL, rssfeed), faviconImage);
// Refresh the number of new items in this feed
refreshNewCount();
}
@Background
protected void refreshNewCount() {
// TODO: Implement
}
}

23
core/src/org/transdroid/core/gui/rss/RssfeedsActivity.java

@ -0,0 +1,23 @@
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById;
import org.transdroid.core.app.settings.ApplicationSettings;
import com.actionbarsherlock.app.SherlockFragmentActivity;
@EActivity(resName = "activity_rssfeeds")
public class RssfeedsActivity extends SherlockFragmentActivity {
// Settings
@Bean
protected ApplicationSettings applicationSettings;
// Contained feeds and items fragments
@FragmentById(resName = "rssfeeds_list")
protected RssfeedsFragment fragmentFeeds;
@FragmentById(resName = "rssitems_list")
protected RssitemsFragment fragmentItems;
}

71
core/src/org/transdroid/core/gui/rss/RssfeedsAdapter.java

@ -0,0 +1,71 @@
package org.transdroid.core.gui.rss;
import java.util.List;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.app.settings.RssfeedSetting;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
/**
* Adapter that contains a list of RSS feed settings.
* @author Eric Kok
*/
@EBean
public class RssfeedsAdapter extends BaseAdapter {
private List<RssfeedSetting> rssfeeds = null;
@RootContext
protected Context context;
/**
* Allows updating the full internal list of feeds at once, replacing the old list
* @param newRssfeeds The new list of RSS feed settings objects
*/
public void update(List<RssfeedSetting> newRssfeeds) {
this.rssfeeds = newRssfeeds;
notifyDataSetChanged();
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
if (rssfeeds == null)
return 0;
return rssfeeds.size();
}
@Override
public RssfeedSetting getItem(int position) {
if (rssfeeds == null)
return null;
return rssfeeds.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RssfeedView rssfeedView;
if (convertView == null) {
rssfeedView = RssfeedView_.build(context);
} else {
rssfeedView = (RssfeedView) convertView;
}
rssfeedView.bind(getItem(position));
return rssfeedView;
}
}

43
core/src/org/transdroid/core/gui/rss/RssfeedsFragment.java

@ -0,0 +1,43 @@
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.app.settings.ApplicationSettings;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.SherlockListView;
/**
* Fragment lists the RSS feeds the user wants to monitor and, if room, the list of items in a feed in a right pane.
* @author Eric Kok
*/
@EFragment(resName = "fragment_rssfeeds")
@OptionsMenu(resName = "fragment_rssfeeds")
public class RssfeedsFragment extends SherlockFragment {
// Settings
@Bean
protected ApplicationSettings applicationSettings;
@Bean
protected RssfeedsAdapter rssfeedsAdapter;
// Views
@ViewById(resName = "rssfeeds_list")
protected SherlockListView feedsList;
@ViewById
protected TextView nosettingsText;
@AfterViews
protected void init() {
feedsList.setAdapter(rssfeedsAdapter);
rssfeedsAdapter.update(applicationSettings.getRssfeedSettings());
}
}

63
core/src/org/transdroid/core/gui/rss/RssitemStatusLayout.java

@ -0,0 +1,63 @@
package org.transdroid.core.gui.rss;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import fr.marvinlabs.widget.CheckableRelativeLayout;
/**
* A relative layout that that is checkable (to be used in a contextual action bar) and shows a coloured bar in the far
* left indicating the view status, that is, if the item is new to the user or was viewed earlier.
* @author Eric Kok
*/
public class RssitemStatusLayout extends CheckableRelativeLayout {
private final float scale = getContext().getResources().getDisplayMetrics().density;
private final int WIDTH = (int) (6 * scale + 0.5f);
private Boolean isNew = null;
private final Paint oldPaint = new Paint();
private final Paint newPaint = new Paint();
private final RectF fullRect = new RectF();
public RssitemStatusLayout(Context context) {
super(context);
initPaints();
setWillNotDraw(false);
}
public RssitemStatusLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initPaints();
setWillNotDraw(false);
}
private void initPaints() {
oldPaint.setColor(0xFF9E9E9E); // Grey
newPaint.setColor(0xFF8ACC12); // Normal green
}
public void setIsNew(Boolean isNew) {
this.isNew = isNew;
this.invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = WIDTH;
fullRect.set(0, 0, width, height);
if (isNew == null) {
return;
}
canvas.drawRect(fullRect, isNew? newPaint: oldPaint);
}
}

37
core/src/org/transdroid/core/gui/rss/RssitemView.java

@ -0,0 +1,37 @@
package org.transdroid.core.gui.rss;
import java.util.Date;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.ifies.android.sax.Item;
import android.content.Context;
import android.text.format.DateUtils;
import android.widget.TextView;
/**
* View that represents some {@link Item} object, which is a single item in some RSS feed.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_rssitem")
public class RssitemView extends RssitemStatusLayout {
// Views
@ViewById
protected TextView nameText, dateText;
public RssitemView(Context context) {
super(context);
}
public void bind(Item rssitem, Date lastViewedItem) {
nameText.setText(rssitem.getTitle());
dateText.setText(DateUtils.getRelativeDateTimeString(getContext(), rssitem.getPubdate().getTime(),
DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_MONTH));
setIsNew(rssitem.getPubdate().after(lastViewedItem));
}
}

83
core/src/org/transdroid/core/gui/rss/RssitemsAdapter.java

@ -0,0 +1,83 @@
package org.transdroid.core.gui.rss;
import java.util.Date;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.ifies.android.sax.Channel;
import org.ifies.android.sax.Item;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
/**
* Adapter that contains a list of {@link Item}s in an RSS feed.
* @author Eric Kok
*/
@EBean
public class RssitemsAdapter extends BaseAdapter {
private Channel rssfeed = null;
private Date lastViewedItem;
@RootContext
protected Context context;
/**
* Allows updating the full RSS feed (channel and contained items), replacing the old data
* @param newRssfeeds The new RSS feed contents
*/
public void update(Channel rssfeed) {
this.rssfeed = rssfeed;
notifyDataSetChanged();
}
/**
* Registers the date that the user last viewed this feed. Any RSS items after this date will be visually marked as
* new.
* @param lastViewedItem The date after which RSS items should be marked as new
*/
public void setLastItemViewed(Date lastViewedItem) {
this.lastViewedItem = lastViewedItem;
notifyDataSetChanged();
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getCount() {
if (rssfeed == null)
return 0;
return rssfeed.getItems().size();
}
@Override
public Item getItem(int position) {
if (rssfeed == null)
return null;
return rssfeed.getItems().get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RssitemView rssitemView;
if (convertView == null) {
rssitemView = RssitemView_.build(context);
} else {
rssitemView = (RssitemView) convertView;
}
rssitemView.bind(getItem(position), lastViewedItem);
return rssitemView;
}
}

49
core/src/org/transdroid/core/gui/rss/RssitemsFragment.java

@ -0,0 +1,49 @@
package org.transdroid.core.gui.rss;
import java.util.Date;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.FragmentArg;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ViewById;
import org.ifies.android.sax.Channel;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.SherlockListView;
/**
* Fragment that lists the items in a specific RSS feed
* @author Eric Kok
*/
@EFragment(resName = "fragment_rssitems")
public class RssitemsFragment extends SherlockFragment {
@FragmentArg
@InstanceState
protected Channel rssfeed;
@FragmentArg
@InstanceState
protected Date lastViewedItem;
// Views
@ViewById(resName = "rssfeeds_list")
protected SherlockListView rssitemsList;
@Bean
protected RssitemsAdapter rssitemsAdapter;
@ViewById
protected TextView emptyText;
@AfterViews
protected void init() {
rssitemsList.setAdapter(rssitemsAdapter);
rssitemsAdapter.setLastItemViewed(lastViewedItem);
rssitemsAdapter.update(rssfeed);
}
}

6
full/AndroidManifest.xml

@ -91,6 +91,12 @@
android:resource="@xml/searchable" /> android:resource="@xml/searchable" />
</activity> </activity>
<!-- RSS -->
<activity
android:name="org.transdroid.core.gui.rss.RssfeedsActivity_"
android:label="@string/action_rss"
android:theme="@style/TransdroidTheme" />
<provider <provider
android:name="org.transdroid.core.gui.SearchHistoryProvider" android:name="org.transdroid.core.gui.SearchHistoryProvider"
android:authorities="org.transdroid.core.gui.SearchHistoryProvider" android:authorities="org.transdroid.core.gui.SearchHistoryProvider"

3
lib/src/org/transdroid/daemon/Synology/SynologyAdapter.java

@ -326,7 +326,8 @@ public class SynologyAdapter implements IDaemonAdapter {
jsonTorrent.getString("title"), jsonTorrent.getString("title"),
new Date(detail.getLong("create_time") * 1000), new Date(detail.getLong("create_time") * 1000),
null, null,
"" "",
settings.getType()
); );
} }

Loading…
Cancel
Save