Eric Kok
12 years ago
24 changed files with 1323 additions and 1 deletions
Binary file not shown.
@ -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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<?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 @@ |
|||||||
|
<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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
/* |
||||||
|
* 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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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