~ubuntu-branches/ubuntu/maverick/sugar-calculate-activity/maverick-201007230209

« back to all changes in this revision

Viewing changes to shareable_activity.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2009-10-25 19:24:50 UTC
  • mfrom: (1.1.4 upstream) (0.4.4 sid)
  • Revision ID: james.westby@ubuntu.com-20091025192450-aprz7n98niflonce
Tags: 30-3
* Update python-sugar.mk to replace superfluous COPYING files with
  symlinks.
* Drop COPYING removal (python-sugar.mk replaces with symlink now).
* Fix python-sugar.mk to include both simple and versioned activity
  packages in DEB_PYTHON_SUGAR_PACKAGES by default. This fixes
  sugar-calculate-activity virtually empty binary package, and closes:
  bug#552332, thanks to Sascha Silbe.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import dbus
 
2
from dbus import Interface
 
3
from dbus.service import method, signal
 
4
import telepathy
 
5
 
 
6
from sugar.activity import activity
 
7
from sugar.presence import presenceservice
 
8
from sugar.presence.sugartubeconn import SugarTubeConnection
 
9
 
 
10
import logging
 
11
_logger = logging.getLogger('ShareableActivity')
 
12
 
 
13
IFACE = 'org.laptop.ShareableActivity'
 
14
 
 
15
class ShareableObject(dbus.service.Object):
 
16
 
 
17
    def __init__(self, tube, path):
 
18
        dbus.service.Object.__init__(self, tube, path)
 
19
 
 
20
    @dbus.service.signal(dbus_interface=IFACE, signature='sv')
 
21
    def SendMessage(self, msg, kwargs):
 
22
        pass
 
23
 
 
24
    @dbus.service.signal(dbus_interface=IFACE, signature='ssv')
 
25
    def SendMessageTo(self, busname, msg, kwargs):
 
26
        pass
 
27
 
 
28
class ShareableActivity(activity.Activity):
 
29
    '''
 
30
    A shareable activity.
 
31
 
 
32
    Signals to connect to for more notifications:
 
33
        self.get_shared_activity().connect('buddy-joined', ...)
 
34
        self.get_shared_activity().connect('buddy-left', ...)
 
35
    '''
 
36
 
 
37
    def __init__(self, handle, *args, **kwargs):
 
38
        '''
 
39
        Initialize the ShareableActivity class.
 
40
 
 
41
        Kwargs:
 
42
            service_path
 
43
        '''
 
44
 
 
45
        activity.Activity.__init__(self, handle, *args, **kwargs)
 
46
 
 
47
        self._sync_hid = None
 
48
        self._message_cbs = {}
 
49
 
 
50
        self._connection = None
 
51
        self._tube_conn = None
 
52
 
 
53
        self._pservice = presenceservice.get_instance()
 
54
        self._owner = self._pservice.get_owner()
 
55
        self._owner_id = str(self._owner._properties['nick'])
 
56
 
 
57
        self._service_path = kwargs.get('service_path',
 
58
            self._generate_service_path())
 
59
        self._dbus_object = None
 
60
 
 
61
        _logger.debug('Setting service name %s, service path %s', \
 
62
            IFACE, self._service_path)
 
63
 
 
64
        self._connect_to_ps()
 
65
 
 
66
    def get_shared_activity(self):
 
67
        '''Get shared_activity object; works for different API versions.'''
 
68
        try:
 
69
            return self.shared_activity
 
70
        except:
 
71
            return self._shared_activity
 
72
 
 
73
    def get_owner(self):
 
74
        '''Return buddy object of the owner.'''
 
75
        return self._owner
 
76
 
 
77
    def get_owner_id(self):
 
78
        '''Return id (nickname) of the owner.'''
 
79
        return self._owner_id
 
80
 
 
81
    def get_bus_name(self):
 
82
        '''
 
83
        Return the DBus bus name for the tube we're using, or None if there
 
84
        is no tube yet.
 
85
        '''
 
86
        if self._tube_conn is not None:
 
87
            return self._tube_conn.get_unique_name()
 
88
        else:
 
89
            return None
 
90
 
 
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)
 
96
 
 
97
    def _connect_to_ps(self):
 
98
        '''
 
99
        Connect to the presence service.
 
100
        '''
 
101
        if self.get_shared_activity():
 
102
            self.connect('joined', self._sa_joined_cb)
 
103
            if self.get_shared():
 
104
                self._sa_joined_cb()
 
105
        else:
 
106
            self.connect('shared', self._sa_shared_cb)
 
107
 
 
108
    def _setup_shared_activity(self):
 
109
        '''
 
110
        Setup sharing stuff: get channels etc.
 
111
        '''
 
112
 
 
113
        sa = self.get_shared_activity()
 
114
        if sa is None:
 
115
            _logger.error('_setup_shared_activity(): no shared_activity yet!')
 
116
            return False
 
117
 
 
118
        self._connection = sa.telepathy_conn
 
119
        self._tubes_chan = sa.telepathy_tubes_chan
 
120
        self._text_chan = sa.telepathy_text_chan
 
121
 
 
122
        self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
 
123
            'NewTube', self._new_tube_cb)
 
124
 
 
125
    def _sa_shared_cb(self, activity):
 
126
        self._setup_shared_activity()
 
127
        id = self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
 
128
            IFACE, {})
 
129
 
 
130
    def _sa_joined_cb(self, activity):
 
131
        """Callback for when we join an existing activity."""
 
132
 
 
133
        _logger.info('Joined existing activity')
 
134
        self._request_sync = True
 
135
        self._setup_shared_activity()
 
136
 
 
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)
 
140
 
 
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)
 
146
 
 
147
    def _list_tubes_error_cb(self, e):
 
148
        _logger.error('ListTubes() failed: %s', e)
 
149
 
 
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,
 
153
                     params, state)
 
154
 
 
155
        if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE):
 
156
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
 
157
                self._tubes_chan[
 
158
                    telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
 
159
 
 
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])
 
164
 
 
165
            self._tube_conn.add_signal_receiver(self._send_message_cb,
 
166
                'SendMessage', sender_keyword='sender')
 
167
 
 
168
            self._dbus_object = ShareableObject(self._tube_conn, \
 
169
                    self._service_path)
 
170
 
 
171
    def buddy_joined(self, activity, buddy):
 
172
        '''
 
173
        Override to take action when a buddy joins.
 
174
        '''
 
175
        _logger.debug('Buddy joined: %s', buddy)
 
176
 
 
177
    def buddy_left(self, activity, buddy):
 
178
        '''
 
179
        Override to take action when a buddy left.
 
180
        '''
 
181
        _logger.debug('Buddy left: %s', buddy)
 
182
 
 
183
    def connect_message(self, msg, func):
 
184
        '''
 
185
        Connect function 'func' so that it's called when message <msg>
 
186
        is received. The function will receive keyword arguments sent
 
187
        with the message.
 
188
        '''
 
189
        self._message_cbs[msg] = func
 
190
 
 
191
    def message_received(self, msg, **kwargs):
 
192
        '''
 
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().
 
196
        '''
 
197
        _logger.debug('Received message: %s(%r)', msg, kwargs)
 
198
 
 
199
    def send_message(self, msg, **kwargs):
 
200
        '''
 
201
        Send a message to all connected buddies.
 
202
        '''
 
203
        if self._dbus_object is not None:
 
204
            _logger.debug('Sending message: %s(%r)', msg, kwargs)
 
205
            self._dbus_object.SendMessage(msg, kwargs)
 
206
        else:
 
207
            _logger.debug('Not shared, not sending message %s(%r)', \
 
208
                    msg, kwargs)
 
209
 
 
210
    def send_message_to(self, buddy, msg, **kwargs):
 
211
        '''
 
212
        Send a message to one particular buddy.
 
213
        '''
 
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)
 
218
        else:
 
219
            _logger.debug('Not shared, not sending message %s(%r) to %s', \
 
220
                msg, kwargs, buddy)
 
221
 
 
222
    def _dispatch_message(self, msg, kwargs):
 
223
        passkwargs = {}
 
224
        for k, v in kwargs.iteritems():
 
225
            passkwargs[str(k)] = v
 
226
 
 
227
        if msg in self._message_cbs:
 
228
            func = self._message_cbs[msg]
 
229
            func(**passkwargs)
 
230
        else:
 
231
            self.message_received(msg, **passkwargs)
 
232
 
 
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():
 
238
            return
 
239
        kwargs['sender'] = sender
 
240
        self._dispatch_message(msg, kwargs)
 
241
 
 
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():
 
245
            return
 
246
        kwargs['sender'] = sender
 
247
        kwargs['to'] = to
 
248
        self._dispatch_message(msg, kwargs)
 
249
 
 
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:
 
253
            return
 
254
 
 
255
        self._syncreq_buddy = 0
 
256
        self._sync_hid = gobject.timeout_add(2000, self._request_sync_cb)
 
257
        self._request_sync_cb()
 
258
 
 
259
    def _request_sync_cb(self):
 
260
        if self._syncreq_buddy <= len(self._connected_buddies):
 
261
            self._sync_hid = None
 
262
            return False
 
263
 
 
264
        self._syncreq_buddy += 1
 
265