Replace enough of the old PS so we can share an activity instance publically

on the network.
This commit is contained in:
Tomeu Vizoso 2010-06-28 16:42:23 +02:00
parent 98cc77f1fb
commit a0b9394846
3 changed files with 165 additions and 77 deletions

View File

@ -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()

View File

@ -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

View File

@ -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))
self.emit("activity-shared", True, activity, None)
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)
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.
Uses the AdvertiseActivity method on the service to ask for the
sharing of the given activity. Arranges to emit activity-shared
event with:
(success, Activity, err)
on success/failure.
returns None
"""
actid = activity.get_id()
if properties is None:
properties = {}
# 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)
if 'id' not in properties:
properties['id'] = activity.get_id()
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 'type' not in properties:
properties['type'] = activity.get_bundle_id()
if 'name' not in properties:
properties['name'] = activity.metadata.get('title', None)
if 'color' not in properties:
properties['color'] = activity.metadata.get('icon-color', None)
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
"""
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