clean:
rm src/*pyo
+ rm src/*pyc
sourcepkg:
dpkg-buildpackage -rfakeroot -sa -S -i -I.git
* Added QML interface
* Fixed the use of id as article identifier (#6473)
* Added categories
+ * Fixed the text of updated articles (#6232)
- -- Yves <yves@marcoz.org> Sun, 31 Oct 2010 28:03:19 -0800
+ -- Yves <yves@marcoz.org> Sun, 21 Nov 2010 21:19:19 -0800
feedingit (0.8.0-9) unstable; urgency=low
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python, python-hildon,
python-dbus, python-osso, python-gconf, python2.5-webkit,
- python-hildondesktop, hildon-desktop-python-loader
+ python-hildondesktop, hildon-desktop-python-loader, qt4-declarative-qmlviewer
Description: A RSS feed reader with portrait mode support
FeedingIt is an easy to use RSS feed reader with portrait
mode support and automatic feed updates. You can swipe left
#!/bin/sh
+
+start_qml() {
+ cd /opt/FeedingIt
+ python2.5 FeedingIt-Web.py 2>&1 >/dev/null &
+ pid=`pidof python2.5 FeedingIt-Web.py`
+ sleep 1
+ qmlviewer -opengl -fullscreen qml/FeedingIt.qml
+ kill $pid
+}
+start_gtk() {
+ cd /opt/FeedingIt
+ python2.5 FeedingIt.py
+}
+
+
case "$1" in
update)
dbus-send --print-reply --dest='org.marcoz.feedingit' --session /org/marcoz/feedingit/update org.marcoz.feedingit.UpdateAll
nice python2.5 update_feeds.py
;;
noqml)
- cd /opt/FeedingIt
- python2.5 FeedingIt.py
+ start_gtk
;;
+qml)
+ start_qml
+ ;;
*)
- cd /opt/FeedingIt
- python2.5 FeedingIt-Web.py 2>&1 >/dev/null &
- pid=`pidof python2.5 FeedingIt-Web.py`
- sleep 1
- qmlviewer -opengl -fullscreen qml/FeedingIt.qml
- kill $pid
+ if [ -f /home/user/.feedingit/noqml ]
+ then
+ start_gtk
+ else
+ start_qml
+ fi
;;
-
esac
\ No newline at end of file
global listing
listing = Listing(CONFIGDIR)
httpd = BaseHTTPServer.HTTPServer(("127.0.0.1", PORT), Handler)
- httpd.serve_forever()
+ httpd.serve_forever()
class App():
def addFeed(self, url):
pass
def openFeed(self, key):
- cat = listing.getFeedCategory(key)
- commands.append( ("openFeed", cat, key) )
+ commands.append( ("openFeed", key) )
- def OpenArticle(self, key, id):
- cat = listing.getFeedCategory(key)
- commands.append( ("openArticle", (cat, key, id)) )
+ def openArticle(self, key, id):
+ commands.append( ("openArticle", key, id) )
+
+ def automaticUpdate(self):
+ commands.append(("updateAll",))
+# for cat in listing.getListOfCategories():
+# for feed in listing.getSortedListOfKeys("Manual", category=cat):
+# feeds.append(feed)
+# download = Download(listing, feeds)
+# download.start()
class Download(Thread):
def __init__(self, listing, keys):
updateDbusHandler.UpdateFinished()
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
- def openTaskSwitch(self):
- import subprocess
- subprocess.Popen("dbus-send /com/nokia/hildon_desktop com.nokia.hildon_desktop.exit_app_view", shell=True)
-
- def automaticUpdate(self):
+ def updateAll(self):
feeds = []
for cat in listing.getListOfCategories():
for feed in listing.getSortedListOfKeys("Manual", category=cat):
feeds.append(feed)
- print feeds
download = Download(listing, feeds)
download.start()
+ def openTaskSwitch(self):
+ import subprocess
+ subprocess.Popen("dbus-send /com/nokia/hildon_desktop com.nokia.hildon_desktop.exit_app_view", shell=True)
+
def getCommands(self):
-
commandXml = "<commands>"
for item in commands:
if item[0]=="addFeed":
commandXml += "<command c='addFeed'>%s</command>" %(sanitize(item[1]))
if item[0]=="openFeed":
- commandXml += "<command c='openFeed' cat='%s'>%s</command>" % (sanitize(item[1]), sanitize(item[2]) )
+ key = item[1]
+ cat = str(listing.getFeedCategory(key))
+ commandXml += "<command c='openFeed' cat='%s'>%s</command>" % (sanitize(cat), sanitize(key) )
if item[0]=="openArticle":
- commandXml += "<command c='openArticle' cat='%s' key='%s'>%s</command>" %(sanitize(item[1], sanitize(item[2][0]), sanitize(item[2][1])) )
+ key = item[1]
+ cat = str(listing.getFeedCategory(key))
+ articleid = item[2]
+ commandXml += "<command c='openArticle' cat='%s' key='%s'>%s</command>" %(sanitize(cat), sanitize(key), sanitize(articleid) )
+ if item[0]=="updateAll":
+ self.updateAll()
commands.remove(item)
commandXml += "</commands>"
return commandXml
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
for cat in listing.getListOfCategories():
xml += "<category>"
- xml += "<catname>%s</catname>" %listing.getCategoryTitle(cat)
+ xml += "<catname>%s</catname>" %sanitize(listing.getCategoryTitle(cat))
xml += "<catid>%s</catid>" % cat
xml += "</category>"
xml += "</xml>"
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
for key in listing.getSortedListOfKeys("Manual", category=catid):
xml += "<feed>"
- xml += "<feedname>%s</feedname>" %listing.getFeedTitle(key)
+ xml += "<feedname>%s</feedname>" %sanitize(listing.getFeedTitle(key))
xml += "<feedid>%s</feedid>" %key
xml += "<unread>%s</unread>" %listing.getFeedNumberOfUnreadItems(key)
xml += "<updatedDate>%s</updatedDate>" %listing.getFeedUpdateTime(key)
download.start()
xml = "<xml>OK</xml>"
elif request[1]=="updateAll":
- self.automaticUpdate()
+ #app.automaticUpdate()
+ self.updateAll()
updateDbusHandler.ArticleCountUpdated()
xml = "<xml>OK</xml>"
elif request[1] == "addCat":
import thread
-#print "serving at port", PORT
+
+
+# Start the HTTP server in a new thread
thread.start_new_thread(start_server, ())
+# Initialize the glib mainloop, for dbus purposes
from feedingitdbus import ServerObject
from updatedbus import UpdateServerObject, get_lock
+
import gobject
gobject.threads_init()
import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+global updateDbusHandler, dbusHandler
app = App()
dbusHandler = ServerObject(app)
updateDbusHandler = UpdateServerObject(app)
+
mainloop = gobject.MainLoop()
mainloop.run()
# Version : 0.6.1
# Description : Simple RSS Reader
# ============================================================================
-try:
- import gtk
- import hildon
- from gobject import idle_add
-except:
- pass
+#try:
+# import gtk
+# import hildon
+# from gobject import idle_add
+#except:
+# pass
from ConfigParser import RawConfigParser
from gconf import client_get_default
self.window.destroy()
def createDialog(self):
-
+ import gtk
+ import hildon
+ from gobject import idle_add
self.window = gtk.Dialog("Settings", self.parent)
self.window.set_geometry_hints(min_height=600)
self.saveConfig()
def selection_changed(self, selector, button, setting):
+ from gobject import idle_add
current_selection = selector.get_current_text()
if current_selection:
self.config[setting] = current_selection
file.close()
def create_selector(self, choices, setting):
+ import gtk
+ import hildon
+ from gobject import idle_add
#self.pickerDialog = hildon.PickerDialog(self.parent)
selector = hildon.TouchSelector(text=True)
index = 0
Rectangle {
id: noArticle
- width: parent.width; height: parent.height;
+ //width: parent.width; height: parent.height;
+ //color: "#000000"
+ anchors.centerIn: parent;
visible: false;
z:8;
- Text { text: qsTr("No articles available"); }
- states: State {
- name: "noArticle"; when: articles.count==0
- PropertyChanges { target: noArticle; visible: true; }
+ Text { id: noText; color: "#ffffff"; anchors.centerIn: parent; text: qsTr("No articles available"); }
+ Image { id: loadingImage; anchors.centerIn: parent; source: "common/images/loading.png";
+ height: 96; width: 96;
+ NumberAnimation on rotation {
+ from: 0; to: 360; running: (loadingImage.visible == true); loops: Animation.Infinite; duration: 900
+ }
}
+
+ states: [ State {
+ name: "noArticle"; when: articles.count==0 && articles.status==XmlListModel.Ready
+ PropertyChanges { target: noArticle; visible: true; }
+ PropertyChanges { target: loadingImage; visible: false; }
+ PropertyChanges { target: noText; visible: true; }
+ }, State {
+ name: "loading"; when: articles.count==0 && articles.status != XmlListModel.Ready
+ PropertyChanges { target: noArticle; visible: true; }
+ PropertyChanges { target: noText; visible: false; }
+ PropertyChanges { target: loadingImage; visible: true; }
+ }
+ ]
}
VisualDataModel {
id: packageItem
Item { id: flipItem; Package.name: 'flip'; width: articleViewer.width; height: articleViewer.height;
- property string url: (articleView.visible && Math.abs(articleView.currentIndex-index)<2) ? "http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid : "";
+ property string url: (articleView.visible && Math.abs(articleView.currentIndex-index)<2) ? path: ""; //http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid : "";
ArticleDisplay {
zoomEnabled: articleViewer.zoomEnabled;
property bool vertPanningEnabled: articleViewer.vertPanningEnabled;
StateChangeScript {
name: "myScript"
script: {
- flipItem.url="http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid;
+ flipItem.url=path; //"http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid;
var doc = new XMLHttpRequest();
var url = "http://localhost:8000/read/" + articleViewer.feedid + "/" + articleid;
//console.log(url)
}, State {
name: 'articleIsClose'; when: articleView.visible && Math.abs(articleView.currentIndex-index)<2;
StateChangeScript {
- script: { flipItem.url="http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid;}
+ script: { flipItem.url=path; } //"http://localhost:8000/html/" + articleViewer.feedid + "/" + articleid;}
}
}
]
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
Item {
+ x: wrapper.ListView.view.width - 128; y: 12
+ height: 58; width: 58;
+ //anchors.horizontalCenter: parent.horizontalCenter;
+ Image { source: "common/images/wmEditIcon.png" }
+ MouseArea {
+ anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
+ }
+ visible: inEditMode
+ }
+ Item {
x: wrapper.ListView.view.width - 64; y: 12
height: 58; width: 58;
//anchors.horizontalCenter: parent.horizontalCenter;
confirmationMessage.state="deleteFeed";
}
+ function feedEdit(feedname, feedid, url) {
+ addFeed.feedEdit = true;
+ addFeed.feedName = feedname;
+ addFeed.feedUrl = url;
+ addFeed.visible = true;
+ }
+
function addCategory(categoryName) {
var doc = new XMLHttpRequest();
var url = "http://localhost:8000/addCat/"+categoryName
doc.open("GET", url);
doc.send();
feedsItem.reload();
- console.log(addFeedDialog.visible)
+ //console.log(addFeedDialog.visible)
addFeedDialog.visible=false;
- console.log(addFeedDialog.visible)
+ //console.log(addFeedDialog.visible)
}
function updateClicked(feedid) {
//var els = xmlDoc.getElementsByTagName("updating");
var isUpdating = xmlDoc.firstChild.firstChild.nodeValue;
- console.log(isUpdating);
+ //console.log(isUpdating);
if (isUpdating=="True") {
toolBar.feedUpdating = true;
} else {
for (var ii = 0; ii < commands.length; ++ii) {
// process the commands
var command = commands[ii].attributes[0].value; //("c")
- console.log(command)
+ //console.log(command)
if (command=="openFeed") {
// Open feed feed
var catid = commands[ii].attributes[1].value;
}
if (command=="openArticle") {
// Open feed and article
+ var catid = commands[ii].attributes[1].value;
var feedid = commands[ii].attributes[2].value; //("key");
var articleid = commands[ii].firstChild.nodeValue;
if (!flipper.visible) {
container.categoryClicked(catid);
container.feedClicked(feedid,false);
- container.articleClicked(articleid, index)
- console.log("art: "+feedid+"/"+articleid);
+ flipper.viewArticle(articleid)
}
}
if (command=="addFeed") {
// Open the addFeed dialog
var url = commands[ii].firstChild.nodeValue;
- console.log("add: "+url)
+ //console.log("add: "+url)
+
}
}
var url = "http://localhost:8000/isUpdating/" + flipper.feedid
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.DONE) {
- var a = doc.responseXML.documentElement;
- console.log(a.firstChild.nodeValue);
- if (a.firstChild.nodeValue=="True") {
+ var xmlDoc = doc.responseXML.documentElement;
+ var isUpdating = xmlDoc.firstChild.firstChild.nodeValue;
+ //console.log(isUpdating);
+ if (isUpdating=="True") {
toolBar.feedUpdating = true;
} else {
if (toolBar.feedUpdating) {
}
}
+// Text {
+// x: container.width/2
+// y:container.height/2
+// text: runtime.orientation;
+// }
+
property bool lockRotation: false
property variant selectedOrientation: Orientation.UnknownOrientation
property variant activeOrientation: selectedOrientation == Orientation.UnknownOrientation ? runtime.orientation : selectedOrientation
+ property variant oldOrientation: Orientation.Portrait
state: "orientation " + activeOrientation
- property bool inPortrait: (activeOrientation == Orientation.Portrait || activeOrientation == Orientation.PortraitInverted);
+ property bool inPortrait: (activeOrientation == Orientation.Portrait || activeOrientation == Orientation.PortraitInverted || activeOrientation==Orientation.UnknownOrientation && (oldOrientation==Orientation.Portrait||oldOrientation==Orientation.PortraitInverted)) ;
// rotation correction for landscape devices like N900
property bool landscapeWindow: screen.width > screen.height
} else if (orientation == Orientation.LandscapeInverted) {
angle = 270;
} else {
- angle = 0;
+ angle = getAngle(oldOrientation);
}
return angle;
}
width: baseHeight
height: baseWidth
}
- StateChangeScript { script: console.log(container.width +"/"+container.height) }
+ StateChangeScript { script: container.oldOrientation=Orientation.Landscape }
},
State {
name: "orientation " + Orientation.PortraitInverted
width: baseWidth
height: baseHeight
}
- StateChangeScript { script: console.log(container.width +"/"+container.height) }
+ StateChangeScript { script: container.oldOrientation=Orientation.PortraitInverted }
},
State {
name: "orientation " + Orientation.LandscapeInverted
width: baseHeight
height: baseWidth
}
- StateChangeScript { script: console.log(container.width +"/"+container.height) }
+ StateChangeScript { script: container.oldOrientation=Orientation.LandscapeInverted }
+ },
+ State {
+ name: "orientation " + Orientation.Portrait
+ PropertyChanges {
+ target: container
+ rotation: getAngle(Orientation.Portrait)+rotationDelta
+ width: baseWidth
+ height: baseHeight
+ }
+ StateChangeScript { script: container.oldOrientation=Orientation.Portrait }
+ },
+ State {
+ name: "orientation 0"
+ PropertyChanges {
+ target: container
+ rotation: getAngle(container.oldOrientation)+rotationDelta
+ width: container.inPortrait ? baseWidth : baseHeight
+ height: container.inPortrait ? baseHeight : baseWidth
+ }
+ //StateChangeScript { }
}
]
transitions: Transition {
XmlRole { name: "updatedDate"; query: "updatedDate/string()" }
XmlRole { name: "icon"; query: "icon/string()" }
XmlRole { name: "updating"; query: "updating/string()"; isKey: true }
+ //XmlRole { name: "url"; query: "url/string()"; }
}
Component {
Text { text: updatedDate + " / " + qsTr("%1 unread items").arg(unread); color: (unread=="0") ? "white" : "#7b97fd"; width: parent.width; font.bold: false; elide: Text.ElideRight; style: Text.Raised; styleColor: "black" }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
+// Item {
+// x: wrapper.ListView.view.width - 128; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/wmEditIcon.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.feedEdit(feedname, feedid, url); }
+// }
+// visible: inEditMode
+// }
+ Item {
+ x: wrapper.ListView.view.width - 64; y: 12
+ height: 58; width: 58;
+ //anchors.horizontalCenter: parent.horizontalCenter;
+ Image { source: "common/images/delete.png" }
+ MouseArea {
+ anchors.fill: parent; onClicked: { container.feedDeleted(feedid); }
+ }
+ visible: inEditMode
+ }
}
MouseArea { anchors.fill: wrapper; onClicked: { container.feedClicked(feedid, updating=="True") } }
}
color: "white"
property alias feedName: feedName.text
property string catid
+ property string feedUrl: feedURL.text
+ //property boolean feedEdit: false;
+
MouseArea { anchors.fill: parent; onClicked: {} }
Column {
Row {
}
state: "update"
states : [State {name: "loading"; when: (feedUpdating);
- PropertyChanges {target: updateFeedButton; imageSource: "images/loading.png" }
+ PropertyChanges {target: updateFeedButton; imageSource: "images/loading2.png" }
}, State { name: "update"; when: (!feedUpdating);
PropertyChanges {target: updateFeedButton; iconRotation: 0}
PropertyChanges {target: updateFeedButton; imageSource: "images/rotate.png"}
id = self.generateUniqueId(tmpEntry)
#articleTime = time.mktime(self.entries[id]["dateTuple"])
- if not id in ids:
- soup = BeautifulSoup(self.getArticle(tmpEntry)) #tmpEntry["content"])
- images = soup('img')
- baseurl = tmpEntry["link"]
- if imageCache:
- for img in images:
- try:
- filename = self.addImage(configdir, self.key, baseurl, img['src'])
- img['src']=filename
+ soup = BeautifulSoup(self.getArticle(tmpEntry)) #tmpEntry["content"])
+ images = soup('img')
+ baseurl = tmpEntry["link"]
+ #if not id in ids:
+ if imageCache:
+ for img in images:
+ try:
+ filename = self.addImage(configdir, self.key, baseurl, img['src'])
+ img['src']="file://%s" %filename
+ count = self.db.execute("SELECT count(1) FROM images where id=? and imagePath=?;", (id, filename )).fetchone()[0]
+ if count == 0:
self.db.execute("INSERT INTO images (id, imagePath) VALUES (?, ?);", (id, filename) )
- except:
- import traceback
- traceback.print_exc()
- print "Error downloading image %s" % img
+ except:
+ import traceback
+ traceback.print_exc()
+ print "Error downloading image %s" % img
tmpEntry["contentLink"] = configdir+self.key+".d/"+id+".html"
file = open(tmpEntry["contentLink"], "w")
file.write(soup.prettify())
file.close()
- values = (id, tmpEntry["title"], tmpEntry["contentLink"], tmpEntry["date"], currentTime, tmpEntry["link"], 0)
- self.db.execute("INSERT INTO feed (id, title, contentLink, date, updated, link, read) VALUES (?, ?, ?, ?, ?, ?, ?);", values)
- else:
- try:
+ if id in ids:
self.db.execute("UPDATE feed SET updated=? WHERE id=?;", (currentTime, id) )
self.db.commit()
- filename = configdir+self.key+".d/"+id+".html"
- file = open(filename,"a")
- utime(filename, None)
- file.close()
- images = self.db.execute("SELECT imagePath FROM images where id=?;", (id, )).fetchall()
- for image in images:
- file = open(image[0],"a")
- utime(image[0], None)
- file.close()
- except:
- pass
+ else:
+ values = (id, tmpEntry["title"], tmpEntry["contentLink"], tmpEntry["date"], currentTime, tmpEntry["link"], 0)
+ self.db.execute("INSERT INTO feed (id, title, contentLink, date, updated, link, read) VALUES (?, ?, ?, ?, ?, ?, ?);", values)
+# else:
+# try:
+# self.db.execute("UPDATE feed SET updated=? WHERE id=?;", (currentTime, id) )
+# self.db.commit()
+# filename = configdir+self.key+".d/"+id+".html"
+# file = open(filename,"a")
+# utime(filename, None)
+# file.close()
+# images = self.db.execute("SELECT imagePath FROM images where id=?;", (id, )).fetchall()
+# for image in images:
+# file = open(image[0],"a")
+# utime(image[0], None)
+# file.close()
+# except:
+# pass
self.db.commit()
text += "<html><head><title>" + title + "</title>"
text += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\n'
#text += '<style> body {-webkit-user-select: none;} </style>'
- text += '</head><body background=\"white\"><div><a href=\"' + link + '\">' + title + "</a>"
+ text += '</head><body bgcolor=\"#ffffff\"><div><a href=\"' + link + '\">' + title + "</a>"
if author != None:
text += "<BR /><small><i>Author: " + author + "</i></small>"
text += "<BR /><small><i>Date: " + date + "</i></small></div>"
date = timegm(feed.getDateTuple(item))
title = feed.getTitle(item)
newId = new_feed.generateUniqueId({"date":date, "title":title})
- values = (newId, title , feed.getContentLink(item), date, time.time(), feed.getExternalLink(item), read_status)
+ values = (newId, title , feed.getContentLink(item), date, tuple(time.time()), feed.getExternalLink(item), read_status)
new_feed.db.execute("INSERT INTO feed (id, title, contentLink, date, updated, link, read) VALUES (?, ?, ?, ?, ?, ?, ?);", values)
new_feed.db.commit()
try:
feed = self.getFeed(key)
db = sqlite3.connect("%s/feeds.db" % self.configdir)
(url, etag, modified) = db.execute("SELECT url, etag, modified FROM feeds WHERE id=?;", (key,) ).fetchone()
- (updateTime, etag, modified) = feed.updateFeed(self.configdir, url, etag, eval(modified), expiryTime, proxy, imageCache)
+ try:
+ modified = time.struct_time(eval(modified))
+ except:
+ modified = None
+ (updateTime, etag, modified) = feed.updateFeed(self.configdir, url, etag, modified, expiryTime, proxy, imageCache)
if updateTime > 0:
- db.execute("UPDATE feeds SET updateTime=?, etag=?, modified=? WHERE id=?;", (updateTime, etag, str(modified), key) )
+ db.execute("UPDATE feeds SET updateTime=?, etag=?, modified=? WHERE id=?;", (updateTime, etag, str(tuple(modified)), key) )
else:
- db.execute("UPDATE feeds SET etag=?, modified=? WHERE id=?;", (etag, str(modified), key) )
+ db.execute("UPDATE feeds SET etag=?, modified=? WHERE id=?;", (etag, str(tuple(modified)), key) )
db.commit()
self.updateUnread(key, db=db)
tmp = "ORDER BY rank"
#keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][0])
if onlyUnread:
- sql = "SELECT id FROM feeds WHERE unread>0 WHERE category=%s" %category + tmp
+ sql = "SELECT id FROM feeds WHERE unread>0 AND category=%s " %category + tmp
else:
sql = "SELECT id FROM feeds WHERE category=%s " %category + tmp
rows = self.db.execute(sql)