Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomeu Vizoso <tomeu.vizoso@collabora.co.uk>2010-06-28 14:42:23 (GMT)
committer Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>2010-08-20 13:33:52 (GMT)
commita0b9394846b5a3306effe5a52857e3bff70294fa (patch)
treebf22012f75ec40d640bc0a043e5f0931a0895981
parent98cc77f1fb35ae0c0e44a27dc389248b8024bba7 (diff)
Replace enough of the old PS so we can share an activity instance publically
on the network.
-rw-r--r--src/sugar/activity/activity.py10
-rw-r--r--src/sugar/presence/activity.py131
-rw-r--r--src/sugar/presence/presenceservice.py99
3 files changed, 164 insertions, 76 deletions
diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py
index 20c9c7d..0c1a206 100644
--- a/src/sugar/activity/activity.py
+++ b/src/sugar/activity/activity.py
@@ -268,7 +268,6 @@ class Activity(Window, gtk.Container):
self._active = False
self._activity_id = handle.activity_id
self.shared_activity = None
- self._share_id = None
self._join_id = None
self._updating_jobject = False
self._closing = False
@@ -639,6 +638,7 @@ class Activity(Window, gtk.Container):
self._jobject.object_id = None
def __privacy_changed_cb(self, shared_activity, param_spec):
+ logging.debug('__privacy_changed_cb %r', shared_activity.props.private)
if shared_activity.props.private:
self._jobject.metadata['share-scope'] = SCOPE_INVITE_ONLY
else:
@@ -670,8 +670,6 @@ class Activity(Window, gtk.Container):
return self.shared_activity.props.joined
def __share_cb(self, ps, success, activity, err):
- self._pservice.disconnect(self._share_id)
- self._share_id = None
if not success:
logging.debug('Share of activity %s failed: %s.',
self._activity_id, err)
@@ -734,9 +732,9 @@ class Activity(Window, gtk.Container):
verb = private and 'private' or 'public'
logging.debug('Requesting %s share of activity %s.', verb,
self._activity_id)
- self._share_id = self._pservice.connect("activity-shared",
- self.__share_cb)
- self._pservice.share_activity(self, private=private)
+ pservice = presenceservice.get_instance()
+ pservice.connect('activity-shared', self.__share_cb)
+ pservice.share_activity(self, private=private)
def _show_keep_failed_dialog(self):
alert = Alert()
diff --git a/src/sugar/presence/activity.py b/src/sugar/presence/activity.py
index 617409f..2c52eea 100644
--- a/src/sugar/presence/activity.py
+++ b/src/sugar/presence/activity.py
@@ -21,6 +21,7 @@ STABLE.
"""
import logging
+from functools import partial
import dbus
import gobject
@@ -33,6 +34,7 @@ from telepathy.interfaces import CHANNEL, \
from telepathy.constants import HANDLE_TYPE_ROOM
CONN_INTERFACE_ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties'
+CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
_logger = logging.getLogger('sugar.presence.activity')
@@ -71,7 +73,13 @@ class Activity(gobject.GObject):
'joined': (bool, None, None, False, gobject.PARAM_READABLE),
}
- def __init__(self, connection, room_handle):
+ def __init__(self, connection, room_handle=None, properties=None):
+ if room_handle is None and properties is None:
+ raise ValueError('Need to pass one of room_handle or properties')
+
+ if properties is None:
+ properties = {}
+
gobject.GObject.__init__(self)
self.telepathy_conn = connection
@@ -79,18 +87,23 @@ class Activity(gobject.GObject):
self.telepathy_tubes_chan = None
self._room_handle = room_handle
- self._id = None
- self._color = None
- self._name = None
- self._type = None
- self._tags = None
- self._private = True
- self._joined = False
+ self._id = properties.get('id', None)
+ self._color = properties.get('color', None)
+ self._name = properties.get('name', None)
+ self._type = properties.get('type', None)
+ self._tags = properties.get('tags', None)
+ self._private = properties.get('private', True)
+ self._joined = properties.get('joined', False)
+
+ self._get_properties_call = None
+ if not self._room_handle is None:
+ self._start_tracking_properties()
+ def _start_tracking_properties(self):
bus = dbus.SessionBus()
self._get_properties_call = bus.call_async(
- connection.requested_bus_name,
- connection.object_path,
+ self.telepathy_conn.requested_bus_name,
+ self.telepathy_conn.object_path,
CONN_INTERFACE_ACTIVITY_PROPERTIES,
'GetProperties',
'u',
@@ -99,17 +112,24 @@ class Activity(gobject.GObject):
error_handler=self._error_handler_cb,
utf8_strings=True)
+ # As only one Activity instance is needed per activity process,
+ # we can afford listening to ActivityPropertiesChanged like this.
+ self.telepathy_conn.connect_to_signal(
+ 'ActivityPropertiesChanged',
+ self.__activity_properties_changed_cb,
+ dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
+
+ def __activity_properties_changed_cb(self, room_handle, properties):
+ _logger.debug('%r: Activity properties changed to %r', self, properties)
+ self._update_properties(properties)
+
def _got_properties_cb(self, properties):
- _logger.debug('_got_properties_cb', properties)
+ _logger.debug('_got_properties_cb %r', properties)
self._get_properties_call = None
self._update_properties(properties)
def _error_handler_cb(self, error):
- _logger.debug('_error_handler_cb', error)
-
- def _properties_changed_cb(self, new_props):
- _logger.debug('%r: Activity properties changed to %r', self, new_props)
- self._update_properties(new_props)
+ _logger.debug('_error_handler_cb %r', error)
def _update_properties(self, new_props):
val = new_props.get('name', self._name)
@@ -169,20 +189,22 @@ class Activity(gobject.GObject):
"""Set a particular property in our property dictionary"""
# FIXME: need an asynchronous API to set these properties,
# particularly 'private'
+
if pspec.name == "name":
- self._activity.SetProperties({'name': val})
self._name = val
elif pspec.name == "color":
- self._activity.SetProperties({'color': val})
self._color = val
elif pspec.name == "tags":
- self._activity.SetProperties({'tags': val})
self._tags = val
elif pspec.name == "private":
- self._activity.SetProperties({'private': val})
self._private = val
+ else:
+ raise ValueError('Unknown property "%s"', pspec.name)
+
+ self._publish_properties()
def set_private(self, val, reply_handler, error_handler):
+ _logger.debug('set_private %r', val)
self._activity.SetProperties({'private': bool(val)},
reply_handler=reply_handler,
error_handler=error_handler)
@@ -263,6 +285,9 @@ class Activity(gobject.GObject):
def set_up_tubes(self, reply_handler, error_handler):
+ if self._room_handle is None:
+ raise ValueError("Don't have a handle for the room yet")
+
chans = []
def tubes_ready():
@@ -327,7 +352,71 @@ class Activity(gobject.GObject):
_logger.debug('%r: joining', self)
self.set_up_tubes(reply_handler=self._join_cb,
- error_handler=self._join_error_cb)
+ error_handler=self._join_error_cb)
+
+ def share(self, share_activity_cb, share_activity_error_cb):
+ if not self._room_handle is None:
+ raise ValueError('Already have a room handle')
+
+ """ TODO: Check we don't need this
+ # We shouldn't have to do this, but Gabble sometimes finds the IRC
+ # transport and goes "that has chatrooms, that'll do nicely". Work
+ # around it til Gabble gets better at finding the MUC service.
+ return '%s@%s' % (activity_id,
+ self._account['fallback-conference-server'])
+ """
+
+ self.telepathy_conn.RequestHandles(
+ HANDLE_TYPE_ROOM,
+ [self._id],
+ reply_handler=partial(self.__got_handles_cb, share_activity_cb, share_activity_error_cb),
+ error_handler=partial(self.__share_error_cb, share_activity_error_cb),
+ dbus_interface=CONNECTION)
+
+ def __got_handles_cb(self, share_activity_cb, share_activity_error_cb, handles):
+ logging.debug('__got_handles_cb %r', handles)
+ self._room_handle = handles[0]
+ self._joined = True
+
+ self.set_up_tubes(
+ partial(self.__tubes_set_up_cb, share_activity_cb, share_activity_error_cb),
+ share_activity_error_cb)
+
+ def __tubes_set_up_cb(self, share_activity_cb, share_activity_error_cb):
+ self.telepathy_conn.AddActivity(
+ self._id,
+ self._room_handle,
+ reply_handler=partial(self.__added_activity_cb, share_activity_cb),
+ error_handler=partial(self.__share_error_cb, share_activity_error_cb),
+ dbus_interface=CONN_INTERFACE_BUDDY_INFO)
+
+ def __added_activity_cb(self, share_activity_cb):
+ self._publish_properties()
+ self._start_tracking_properties()
+ share_activity_cb(self)
+
+ def _publish_properties(self):
+ properties = {}
+
+ if self._color is not None:
+ properties['color'] = self._color
+ if self._name is not None:
+ properties['name'] = self._name
+ if self._type is not None:
+ properties['type'] = self._type
+ if self._tags is not None:
+ properties['tags'] = self._tags
+ properties['private'] = self._private
+
+ logging.debug('_publish_properties calling SetProperties')
+ self.telepathy_conn.SetProperties(
+ self._room_handle,
+ properties,
+ dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
+
+ def __share_error_cb(self, share_activity_error_cb, error):
+ logging.debug('%r: Share failed because: %s', self, error)
+ share_activity_error_cb(self, error)
# GetChannels() wrapper
diff --git a/src/sugar/presence/presenceservice.py b/src/sugar/presence/presenceservice.py
index 0b94924..b36f469 100644
--- a/src/sugar/presence/presenceservice.py
+++ b/src/sugar/presence/presenceservice.py
@@ -68,6 +68,7 @@ class PresenceService(gobject.GObject):
"""
gobject.GObject.__init__(self)
+ self._activity_cache = None
self._buddy_cache = {}
def _new_object(self, object_path):
@@ -249,12 +250,20 @@ class PresenceService(gobject.GObject):
returns single Activity object or None if the activity
is not found using GetActivityById on the service
"""
- for connection in get_connection_manager().connections:
- try:
- room_handle = connection.GetActivity(activity_id)
- return Activity(connection, room_handle)
- except:
- pass
+ if self._activity_cache is not None:
+ if self._activity_cache.props.id != activity_id:
+ raise RuntimeError('Activities can only access their own shared'
+ 'instance')
+ return self._activity_cache
+ else:
+ for connection in get_connection_manager().connections:
+ try:
+ room_handle = connection.GetActivity(activity_id)
+ activity = Activity(connection, room_handle)
+ self._activity_cache = activity
+ return activity
+ except:
+ pass
return None
@@ -351,64 +360,56 @@ class PresenceService(gobject.GObject):
"""Retrieves the laptop Buddy object."""
return Owner()
- def _share_activity_cb(self, activity, op):
+ def __share_activity_cb(self, activity):
"""Finish sharing the activity
"""
- # FIXME find a better way to shutup pylint
- psact = self._new_object(op)
- psact._joined = True
- _logger.debug('%r: Just shared, setting up tubes', activity)
- psact.set_up_tubes(reply_handler=lambda:
- self.emit("activity-shared", True, psact, None),
- error_handler=lambda e:
- self._share_activity_error_cb(activity, e))
-
- def _share_activity_error_cb(self, activity, err):
- """Notify with GObject event of unsuccessful sharing of activity"""
- _logger.debug('Error sharing activity %s: %s', activity.get_id(),
- err)
- self.emit("activity-shared", False, None, err)
+ self.emit("activity-shared", True, activity, None)
+
+ def __share_activity_error_cb(self, activity, error):
+ """Notify with GObject event of unsuccessful sharing of activity
+ """
+ self.emit("activity-shared", False, activity, error)
def share_activity(self, activity, properties=None, private=True):
- """Ask presence service to ask the activity to share itself publicly.
+ if properties is None:
+ properties = {}
- Uses the AdvertiseActivity method on the service to ask for the
- sharing of the given activity. Arranges to emit activity-shared
- event with:
+ if 'id' not in properties:
+ properties['id'] = activity.get_id()
- (success, Activity, err)
+ if 'type' not in properties:
+ properties['type'] = activity.get_bundle_id()
- on success/failure.
+ if 'name' not in properties:
+ properties['name'] = activity.metadata.get('title', None)
- returns None
- """
- actid = activity.get_id()
+ if 'color' not in properties:
+ properties['color'] = activity.metadata.get('icon-color', None)
- if properties is None:
- properties = {}
+ properties['private'] = private
+
+ if self._activity_cache is not None:
+ raise ValueError('Activity %s is already tracked', activity.get_id())
+
+ connection = get_connection_manager().get_preferred_connection()
+ shared_activity = Activity(connection, properties=properties)
+ self._activity_cache = shared_activity
- # Ensure the activity is not already shared/joined
- for obj in self._objcache.values():
- if not isinstance(object, Activity):
- continue
- if obj.props.id == actid or obj.props.joined:
- raise RuntimeError("Activity %s is already shared." %
- actid)
-
- atype = activity.get_bundle_id()
- name = activity.props.title
- properties['private'] = bool(private)
- self._ps.ShareActivity(actid, atype, name, properties,
- reply_handler=lambda op: \
- self._share_activity_cb(activity, op),
- error_handler=lambda e: \
- self._share_activity_error_cb(activity, e))
+ """
+ if shared_activity.props.joined:
+ raise RuntimeError('Activity %s is already shared.' %
+ activity.get_id())
+ """
+
+ shared_activity.share(self.__share_activity_cb,
+ self.__share_activity_error_cb)
def get_preferred_connection(self):
"""Gets the preferred telepathy connection object that an activity
should use when talking directly to telepathy
- returns the bus name and the object path of the Telepathy connection"""
+ returns the bus name and the object path of the Telepathy connection
+ """
connection = get_connection_manager().get_preferred_connection()
if connection is None:
return None