Replace enough of the old PS so we can share an activity instance publically
on the network.
This commit is contained in:
parent
98cc77f1fb
commit
a0b9394846
@ -268,7 +268,6 @@ class Activity(Window, gtk.Container):
|
|||||||
self._active = False
|
self._active = False
|
||||||
self._activity_id = handle.activity_id
|
self._activity_id = handle.activity_id
|
||||||
self.shared_activity = None
|
self.shared_activity = None
|
||||||
self._share_id = None
|
|
||||||
self._join_id = None
|
self._join_id = None
|
||||||
self._updating_jobject = False
|
self._updating_jobject = False
|
||||||
self._closing = False
|
self._closing = False
|
||||||
@ -639,6 +638,7 @@ class Activity(Window, gtk.Container):
|
|||||||
self._jobject.object_id = None
|
self._jobject.object_id = None
|
||||||
|
|
||||||
def __privacy_changed_cb(self, shared_activity, param_spec):
|
def __privacy_changed_cb(self, shared_activity, param_spec):
|
||||||
|
logging.debug('__privacy_changed_cb %r', shared_activity.props.private)
|
||||||
if shared_activity.props.private:
|
if shared_activity.props.private:
|
||||||
self._jobject.metadata['share-scope'] = SCOPE_INVITE_ONLY
|
self._jobject.metadata['share-scope'] = SCOPE_INVITE_ONLY
|
||||||
else:
|
else:
|
||||||
@ -670,8 +670,6 @@ class Activity(Window, gtk.Container):
|
|||||||
return self.shared_activity.props.joined
|
return self.shared_activity.props.joined
|
||||||
|
|
||||||
def __share_cb(self, ps, success, activity, err):
|
def __share_cb(self, ps, success, activity, err):
|
||||||
self._pservice.disconnect(self._share_id)
|
|
||||||
self._share_id = None
|
|
||||||
if not success:
|
if not success:
|
||||||
logging.debug('Share of activity %s failed: %s.',
|
logging.debug('Share of activity %s failed: %s.',
|
||||||
self._activity_id, err)
|
self._activity_id, err)
|
||||||
@ -734,9 +732,9 @@ class Activity(Window, gtk.Container):
|
|||||||
verb = private and 'private' or 'public'
|
verb = private and 'private' or 'public'
|
||||||
logging.debug('Requesting %s share of activity %s.', verb,
|
logging.debug('Requesting %s share of activity %s.', verb,
|
||||||
self._activity_id)
|
self._activity_id)
|
||||||
self._share_id = self._pservice.connect("activity-shared",
|
pservice = presenceservice.get_instance()
|
||||||
self.__share_cb)
|
pservice.connect('activity-shared', self.__share_cb)
|
||||||
self._pservice.share_activity(self, private=private)
|
pservice.share_activity(self, private=private)
|
||||||
|
|
||||||
def _show_keep_failed_dialog(self):
|
def _show_keep_failed_dialog(self):
|
||||||
alert = Alert()
|
alert = Alert()
|
||||||
|
@ -21,6 +21,7 @@ STABLE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
import gobject
|
import gobject
|
||||||
@ -33,6 +34,7 @@ from telepathy.interfaces import CHANNEL, \
|
|||||||
from telepathy.constants import HANDLE_TYPE_ROOM
|
from telepathy.constants import HANDLE_TYPE_ROOM
|
||||||
|
|
||||||
CONN_INTERFACE_ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties'
|
CONN_INTERFACE_ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties'
|
||||||
|
CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
|
||||||
|
|
||||||
_logger = logging.getLogger('sugar.presence.activity')
|
_logger = logging.getLogger('sugar.presence.activity')
|
||||||
|
|
||||||
@ -71,7 +73,13 @@ class Activity(gobject.GObject):
|
|||||||
'joined': (bool, None, None, False, gobject.PARAM_READABLE),
|
'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)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
self.telepathy_conn = connection
|
self.telepathy_conn = connection
|
||||||
@ -79,18 +87,23 @@ class Activity(gobject.GObject):
|
|||||||
self.telepathy_tubes_chan = None
|
self.telepathy_tubes_chan = None
|
||||||
|
|
||||||
self._room_handle = room_handle
|
self._room_handle = room_handle
|
||||||
self._id = None
|
self._id = properties.get('id', None)
|
||||||
self._color = None
|
self._color = properties.get('color', None)
|
||||||
self._name = None
|
self._name = properties.get('name', None)
|
||||||
self._type = None
|
self._type = properties.get('type', None)
|
||||||
self._tags = None
|
self._tags = properties.get('tags', None)
|
||||||
self._private = True
|
self._private = properties.get('private', True)
|
||||||
self._joined = False
|
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()
|
bus = dbus.SessionBus()
|
||||||
self._get_properties_call = bus.call_async(
|
self._get_properties_call = bus.call_async(
|
||||||
connection.requested_bus_name,
|
self.telepathy_conn.requested_bus_name,
|
||||||
connection.object_path,
|
self.telepathy_conn.object_path,
|
||||||
CONN_INTERFACE_ACTIVITY_PROPERTIES,
|
CONN_INTERFACE_ACTIVITY_PROPERTIES,
|
||||||
'GetProperties',
|
'GetProperties',
|
||||||
'u',
|
'u',
|
||||||
@ -99,17 +112,24 @@ class Activity(gobject.GObject):
|
|||||||
error_handler=self._error_handler_cb,
|
error_handler=self._error_handler_cb,
|
||||||
utf8_strings=True)
|
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):
|
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._get_properties_call = None
|
||||||
self._update_properties(properties)
|
self._update_properties(properties)
|
||||||
|
|
||||||
def _error_handler_cb(self, error):
|
def _error_handler_cb(self, error):
|
||||||
_logger.debug('_error_handler_cb', error)
|
_logger.debug('_error_handler_cb %r', error)
|
||||||
|
|
||||||
def _properties_changed_cb(self, new_props):
|
|
||||||
_logger.debug('%r: Activity properties changed to %r', self, new_props)
|
|
||||||
self._update_properties(new_props)
|
|
||||||
|
|
||||||
def _update_properties(self, new_props):
|
def _update_properties(self, new_props):
|
||||||
val = new_props.get('name', self._name)
|
val = new_props.get('name', self._name)
|
||||||
@ -169,20 +189,22 @@ class Activity(gobject.GObject):
|
|||||||
"""Set a particular property in our property dictionary"""
|
"""Set a particular property in our property dictionary"""
|
||||||
# FIXME: need an asynchronous API to set these properties,
|
# FIXME: need an asynchronous API to set these properties,
|
||||||
# particularly 'private'
|
# particularly 'private'
|
||||||
|
|
||||||
if pspec.name == "name":
|
if pspec.name == "name":
|
||||||
self._activity.SetProperties({'name': val})
|
|
||||||
self._name = val
|
self._name = val
|
||||||
elif pspec.name == "color":
|
elif pspec.name == "color":
|
||||||
self._activity.SetProperties({'color': val})
|
|
||||||
self._color = val
|
self._color = val
|
||||||
elif pspec.name == "tags":
|
elif pspec.name == "tags":
|
||||||
self._activity.SetProperties({'tags': val})
|
|
||||||
self._tags = val
|
self._tags = val
|
||||||
elif pspec.name == "private":
|
elif pspec.name == "private":
|
||||||
self._activity.SetProperties({'private': val})
|
|
||||||
self._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):
|
def set_private(self, val, reply_handler, error_handler):
|
||||||
|
_logger.debug('set_private %r', val)
|
||||||
self._activity.SetProperties({'private': bool(val)},
|
self._activity.SetProperties({'private': bool(val)},
|
||||||
reply_handler=reply_handler,
|
reply_handler=reply_handler,
|
||||||
error_handler=error_handler)
|
error_handler=error_handler)
|
||||||
@ -263,6 +285,9 @@ class Activity(gobject.GObject):
|
|||||||
|
|
||||||
def set_up_tubes(self, reply_handler, error_handler):
|
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 = []
|
chans = []
|
||||||
|
|
||||||
def tubes_ready():
|
def tubes_ready():
|
||||||
@ -327,7 +352,71 @@ class Activity(gobject.GObject):
|
|||||||
_logger.debug('%r: joining', self)
|
_logger.debug('%r: joining', self)
|
||||||
|
|
||||||
self.set_up_tubes(reply_handler=self._join_cb,
|
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
|
# GetChannels() wrapper
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ class PresenceService(gobject.GObject):
|
|||||||
"""
|
"""
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
|
self._activity_cache = None
|
||||||
self._buddy_cache = {}
|
self._buddy_cache = {}
|
||||||
|
|
||||||
def _new_object(self, object_path):
|
def _new_object(self, object_path):
|
||||||
@ -249,12 +250,20 @@ class PresenceService(gobject.GObject):
|
|||||||
returns single Activity object or None if the activity
|
returns single Activity object or None if the activity
|
||||||
is not found using GetActivityById on the service
|
is not found using GetActivityById on the service
|
||||||
"""
|
"""
|
||||||
for connection in get_connection_manager().connections:
|
if self._activity_cache is not None:
|
||||||
try:
|
if self._activity_cache.props.id != activity_id:
|
||||||
room_handle = connection.GetActivity(activity_id)
|
raise RuntimeError('Activities can only access their own shared'
|
||||||
return Activity(connection, room_handle)
|
'instance')
|
||||||
except:
|
return self._activity_cache
|
||||||
pass
|
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
|
return None
|
||||||
|
|
||||||
@ -351,64 +360,56 @@ class PresenceService(gobject.GObject):
|
|||||||
"""Retrieves the laptop Buddy object."""
|
"""Retrieves the laptop Buddy object."""
|
||||||
return Owner()
|
return Owner()
|
||||||
|
|
||||||
def _share_activity_cb(self, activity, op):
|
def __share_activity_cb(self, activity):
|
||||||
"""Finish sharing the activity
|
"""Finish sharing the activity
|
||||||
"""
|
"""
|
||||||
# FIXME find a better way to shutup pylint
|
self.emit("activity-shared", True, activity, None)
|
||||||
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):
|
def __share_activity_error_cb(self, activity, error):
|
||||||
"""Notify with GObject event of unsuccessful sharing of activity"""
|
"""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, activity, error)
|
||||||
self.emit("activity-shared", False, None, err)
|
|
||||||
|
|
||||||
def share_activity(self, activity, properties=None, private=True):
|
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:
|
if properties is None:
|
||||||
properties = {}
|
properties = {}
|
||||||
|
|
||||||
# Ensure the activity is not already shared/joined
|
if 'id' not in properties:
|
||||||
for obj in self._objcache.values():
|
properties['id'] = activity.get_id()
|
||||||
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()
|
if 'type' not in properties:
|
||||||
name = activity.props.title
|
properties['type'] = activity.get_bundle_id()
|
||||||
properties['private'] = bool(private)
|
|
||||||
self._ps.ShareActivity(actid, atype, name, properties,
|
if 'name' not in properties:
|
||||||
reply_handler=lambda op: \
|
properties['name'] = activity.metadata.get('title', None)
|
||||||
self._share_activity_cb(activity, op),
|
|
||||||
error_handler=lambda e: \
|
if 'color' not in properties:
|
||||||
self._share_activity_error_cb(activity, e))
|
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):
|
def get_preferred_connection(self):
|
||||||
"""Gets the preferred telepathy connection object that an activity
|
"""Gets the preferred telepathy connection object that an activity
|
||||||
should use when talking directly to telepathy
|
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()
|
connection = get_connection_manager().get_preferred_connection()
|
||||||
if connection is None:
|
if connection is None:
|
||||||
return None
|
return None
|
||||||
|
Loading…
Reference in New Issue
Block a user