Eric Kok
12 years ago
24 changed files with 1323 additions and 1 deletions
Binary file not shown.
@ -0,0 +1,16 @@
@@ -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> |
@ -0,0 +1,16 @@
@@ -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> |
@ -0,0 +1,28 @@
@@ -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> |
@ -0,0 +1,28 @@
@@ -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> |
@ -0,0 +1,46 @@
@@ -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> |
@ -0,0 +1,29 @@
@@ -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> |
@ -0,0 +1,7 @@
@@ -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> |
@ -0,0 +1,149 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,150 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,146 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,237 @@
@@ -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; |
||||
|
||||
} |
@ -0,0 +1,63 @@
@@ -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
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,23 @@
@@ -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; |
||||
|
||||
} |
@ -0,0 +1,71 @@
@@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@
@@ -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()); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,63 @@
@@ -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); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,37 @@
@@ -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)); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,83 @@
@@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,49 @@
@@ -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); |
||||
|
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue