1 # -*- coding: utf-8 -*-
4 A library to post events to the MeeGo 1.2 Harmattan Event Feed
6 This library is intended to be used by N950, N9 application or
7 service developers who want to post their own content to the
8 MeeGo 1.2 Harmattan UX Event Feed screen.
12 Copyright (c) 2011, Thomas Perl <m@thp.io>
14 Permission to use, copy, modify, and/or distribute this software for any
15 purpose with or without fee is hereby granted, provided that the above
16 copyright notice and this permission notice appear in all copies.
18 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 __author__ = 'Thomas Perl <thp.io/about>'
29 __url__ = 'http://thp.io/2011/eventfeed/'
31 __version_info__ = tuple(int(x) for x in __version__.split('.'))
33 # Dependency on PySide for encoding/decoding like MRemoteAction
34 from PySide.QtCore import QBuffer, QIODevice, QDataStream, QByteArray
36 # Python D-Bus Library dependency for communcating with the service
46 logger = logging.getLogger(__name__)
48 # When the user clicks on "Refresh", this signal gets sent via D-Bus:
49 # signal sender=:1.8 -> dest=(null destination) serial=855 path=/eventfeed; interface=com.nokia.home.EventFeed; member=refreshRequested
50 # TODO: Implement support for receiving this signal
52 # MRemoteAction::toString()
53 # http://apidocs.meego.com/1.0/mtf/mremoteaction_8cpp_source.html
54 def qvariant_encode(value):
56 buffer.open(QIODevice.ReadWrite)
57 stream = QDataStream(buffer)
58 stream.writeQVariant(value)
60 return buffer.buffer().toBase64().data().strip()
62 # MRemoteAction::fromString()
63 # http://apidocs.meego.com/1.0/mtf/mremoteaction_8cpp_source.html
64 def qvariant_decode(data):
65 byteArray = QByteArray.fromBase64(data)
66 buffer = QBuffer(byteArray)
67 buffer.open(QIODevice.ReadOnly)
68 stream = QDataStream(buffer)
69 result = stream.readQVariant()
74 class EventFeedItem(object):
75 """One item that can be posted to the event feed"""
77 def __init__(self, icon, title, timestamp=None):
78 """Create a new event feed item
80 :param icon: Icon name or path to icon file (can be a URL)
81 :param title: The title text describing this item
82 :param timestamp: datetime.datetime object when the item happened (optional)
85 timestamp = datetime.datetime.now()
87 timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
92 'timestamp': timestamp,
95 # ID assigned when showing item
98 # Callback for when the action is clicked
101 # Action data (custom list of stuff for callback)
102 self.action_data = None
104 def set_body(self, body):
105 """Body text of the item (string)"""
106 self.args['body'] = body
108 def set_image_list(self, image_list):
109 """List of image filenames/URLs (list of strings)"""
110 self.args['imageList'] = image_list
112 def set_footer(self, footer):
113 """Footer text, displayed near the time (string)"""
114 self.args['footer'] = footer
116 def set_video(self, video):
117 """Flag to overlay a play button on the thumbnail (bool)"""
118 self.args['video'] = video
120 def set_url(self, url):
121 """The URL to be opened when the item is clicked (string)"""
122 self.args['url'] = url
124 def set_action_data(self, *args):
125 """The data to be sent when clicked (list of str, int, bool)"""
126 self.action_data = args
128 def set_custom_action(self, callback):
129 """The action to be executed when clicked (callable)"""
130 self.callback = callback
132 class EventFeedSender:
133 EVENT_FEED_NAME = 'com.nokia.home.EventFeed'
134 EVENT_FEED_PATH = '/eventfeed'
135 EVENT_FEED_INTF = 'com.nokia.home.EventFeed'
136 EVENT_FEED_CALL = 'addItem'
138 DEFAULT_NAME = 'org.maemo.feedingit'
139 DEFAULT_PATH = '/org/maemo/feedingit'
140 DEFAULT_INTF = 'org.maemo.feedingit'
142 def __init__(self, source_name, source_display_name, on_data_received=None):
143 self.next_action_id = 1
145 self.source_name = source_name
146 self.source_display_name = source_display_name
147 self.on_data_received = on_data_received
149 dbus_main_loop = dbus.glib.DBusGMainLoop(set_as_default=True)
150 session_bus = dbus.SessionBus(dbus_main_loop)
152 o = session_bus.get_object(self.EVENT_FEED_NAME, self.EVENT_FEED_PATH)
153 self.event_feed = dbus.Interface(o, self.EVENT_FEED_INTF)
155 def add_item(self, item):
156 """Send a EventFeedItem to the service to be displayed
158 :param item: EventFeedItem to be displayed
161 logger.debug('Message %d already shown - updating footer.', item.id)
162 self.update_item(item)
165 action = item.callback
166 action_data = item.action_data
167 data = item.args.copy()
169 data['sourceName'] = self.source_name
170 data['sourceDisplayName'] = self.source_display_name
178 remote_action.append('OpenFeed')
179 remote_action.extend([qvariant_encode(x) for x in action_data])
181 data['action'] = ' '.join(remote_action)
183 item.id = self.event_feed.addItem(data)
187 def remove_items(self):
188 """Remove all items """
189 self.event_feed.removeItemsBySourceName(self.source_name)
190 # No need to remember action IDs, because all items were removed
193 def remove_item(self, id):
194 """Remove a specific item """
195 self.event_feed.removeItem(id)