2
from dbus import Interface
3
from dbus.service import method, signal
6
from sugar.activity import activity
7
from sugar.presence import presenceservice
8
from sugar.presence.sugartubeconn import SugarTubeConnection
11
_logger = logging.getLogger('ShareableActivity')
13
IFACE = 'org.laptop.ShareableActivity'
15
class ShareableObject(dbus.service.Object):
17
def __init__(self, tube, path):
18
dbus.service.Object.__init__(self, tube, path)
20
@dbus.service.signal(dbus_interface=IFACE, signature='sv')
21
def SendMessage(self, msg, kwargs):
24
@dbus.service.signal(dbus_interface=IFACE, signature='ssv')
25
def SendMessageTo(self, busname, msg, kwargs):
28
class ShareableActivity(activity.Activity):
32
Signals to connect to for more notifications:
33
self.get_shared_activity().connect('buddy-joined', ...)
34
self.get_shared_activity().connect('buddy-left', ...)
37
def __init__(self, handle, *args, **kwargs):
39
Initialize the ShareableActivity class.
45
activity.Activity.__init__(self, handle, *args, **kwargs)
48
self._message_cbs = {}
50
self._connection = None
51
self._tube_conn = None
53
self._pservice = presenceservice.get_instance()
54
self._owner = self._pservice.get_owner()
55
self._owner_id = str(self._owner._properties['nick'])
57
self._service_path = kwargs.get('service_path',
58
self._generate_service_path())
59
self._dbus_object = None
61
_logger.debug('Setting service name %s, service path %s', \
62
IFACE, self._service_path)
66
def get_shared_activity(self):
67
'''Get shared_activity object; works for different API versions.'''
69
return self.shared_activity
71
return self._shared_activity
74
'''Return buddy object of the owner.'''
77
def get_owner_id(self):
78
'''Return id (nickname) of the owner.'''
81
def get_bus_name(self):
83
Return the DBus bus name for the tube we're using, or None if there
86
if self._tube_conn is not None:
87
return self._tube_conn.get_unique_name()
91
def _generate_service_path(self):
92
bundle_id = self.get_bundle_id()
93
last = bundle_id.split('.')[-1]
94
instance_id = self.get_id()
95
return '/org/laptop/ShareableActivity/%s/%s' % (last, instance_id)
97
def _connect_to_ps(self):
99
Connect to the presence service.
101
if self.get_shared_activity():
102
self.connect('joined', self._sa_joined_cb)
103
if self.get_shared():
106
self.connect('shared', self._sa_shared_cb)
108
def _setup_shared_activity(self):
110
Setup sharing stuff: get channels etc.
113
sa = self.get_shared_activity()
115
_logger.error('_setup_shared_activity(): no shared_activity yet!')
118
self._connection = sa.telepathy_conn
119
self._tubes_chan = sa.telepathy_tubes_chan
120
self._text_chan = sa.telepathy_text_chan
122
self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
123
'NewTube', self._new_tube_cb)
125
def _sa_shared_cb(self, activity):
126
self._setup_shared_activity()
127
id = self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
130
def _sa_joined_cb(self, activity):
131
"""Callback for when we join an existing activity."""
133
_logger.info('Joined existing activity')
134
self._request_sync = True
135
self._setup_shared_activity()
137
self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( \
138
reply_handler=self._list_tubes_reply_cb,
139
error_handler=self._list_tubes_error_cb)
141
def _list_tubes_reply_cb(self, tubes):
142
"""Callback for when requesting an existing tube"""
143
_logger.debug('_list_tubes_reply_cb(): %r', tubes)
144
for tube_info in tubes:
145
self._new_tube_cb(*tube_info)
147
def _list_tubes_error_cb(self, e):
148
_logger.error('ListTubes() failed: %s', e)
150
def _new_tube_cb(self, id, initiator, type, service, params, state):
151
_logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
152
'params=%r state=%d', id, initiator, type, service,
155
if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE):
156
if state == telepathy.TUBE_STATE_LOCAL_PENDING:
158
telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
160
self._tube_conn = SugarTubeConnection(self._connection,
161
self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES],
162
id, group_iface=self._text_chan[
163
telepathy.CHANNEL_INTERFACE_GROUP])
165
self._tube_conn.add_signal_receiver(self._send_message_cb,
166
'SendMessage', sender_keyword='sender')
168
self._dbus_object = ShareableObject(self._tube_conn, \
171
def buddy_joined(self, activity, buddy):
173
Override to take action when a buddy joins.
175
_logger.debug('Buddy joined: %s', buddy)
177
def buddy_left(self, activity, buddy):
179
Override to take action when a buddy left.
181
_logger.debug('Buddy left: %s', buddy)
183
def connect_message(self, msg, func):
185
Connect function 'func' so that it's called when message <msg>
186
is received. The function will receive keyword arguments sent
189
self._message_cbs[msg] = func
191
def message_received(self, msg, **kwargs):
193
Override to take action when a message is received.
194
This function will not be called for message handlers already
195
registered with connect_message().
197
_logger.debug('Received message: %s(%r)', msg, kwargs)
199
def send_message(self, msg, **kwargs):
201
Send a message to all connected buddies.
203
if self._dbus_object is not None:
204
_logger.debug('Sending message: %s(%r)', msg, kwargs)
205
self._dbus_object.SendMessage(msg, kwargs)
207
_logger.debug('Not shared, not sending message %s(%r)', \
210
def send_message_to(self, buddy, msg, **kwargs):
212
Send a message to one particular buddy.
214
if self._dbus_object is not None:
215
_logger.debug('Sending message to %s: %s(%r)', buddy, msg, kwargs)
216
#FIXME: convert to busname
217
self._dbus_object.SendMessageTo(buddy, msg, kwargs)
219
_logger.debug('Not shared, not sending message %s(%r) to %s', \
222
def _dispatch_message(self, msg, kwargs):
224
for k, v in kwargs.iteritems():
225
passkwargs[str(k)] = v
227
if msg in self._message_cbs:
228
func = self._message_cbs[msg]
231
self.message_received(msg, **passkwargs)
233
def _send_message_cb(self, msg, kwargs, sender=None):
234
'''Callback to filter message signals.'''
235
_logger.debug('Sender: %s, owner: %s, owner_id: %s, busname: %s', sender, \
236
self.get_owner(), self.get_owner_id(), self.get_bus_name())
237
if sender == self.get_bus_name():
239
kwargs['sender'] = sender
240
self._dispatch_message(msg, kwargs)
242
def _send_message_to_cb(self, to, msg, kwargs, sender=None):
243
'''Callback to filter message signals.'''
244
if to != self.get_bus_name():
246
kwargs['sender'] = sender
248
self._dispatch_message(msg, kwargs)
250
# FIXME: build a standard system to sync state from a single buddy
251
def request_sync(self):
252
if self._sync_hid is not None:
255
self._syncreq_buddy = 0
256
self._sync_hid = gobject.timeout_add(2000, self._request_sync_cb)
257
self._request_sync_cb()
259
def _request_sync_cb(self):
260
if self._syncreq_buddy <= len(self._connected_buddies):
261
self._sync_hid = None
264
self._syncreq_buddy += 1