Replace enough of the old PS so we can join an activity instance on the network.
* src/sugar/activity/activity.py: Get the shared activity wrapper from sugar.presence. * src/sugar/activity/activityfactory.py: Disable checking in the PS for activity_id duplicates. * src/sugar/presence/activity.py: Remove the PS dependency and query Telepathy directly. Implemented enough to join an activity. * src/sugar/presence/buddy.py: Remove the PS dependency and query Telepathy directly. Implemented enough to join an activity. Added an Owner subclass of Buddy() * src/sugar/presence/presenceservice.py: Remove the PS dependency and query Telepathy directly. Implemented enough to join an activity. * src/sugar/presence/util.py: Add ConnectionManager for discovering and tracking connections.
This commit is contained in:
parent
d6da506dfd
commit
98cc77f1fb
@ -70,6 +70,7 @@ from sugar.graphics.alert import Alert
|
|||||||
from sugar.graphics.icon import Icon
|
from sugar.graphics.icon import Icon
|
||||||
from sugar.datastore import datastore
|
from sugar.datastore import datastore
|
||||||
from sugar.session import XSMPClient
|
from sugar.session import XSMPClient
|
||||||
|
from sugar.presence import presenceservice
|
||||||
from sugar import wm
|
from sugar import wm
|
||||||
|
|
||||||
# support deprecated imports
|
# support deprecated imports
|
||||||
@ -266,7 +267,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._pservice = presenceservice.get_instance()
|
|
||||||
self.shared_activity = None
|
self.shared_activity = None
|
||||||
self._share_id = None
|
self._share_id = None
|
||||||
self._join_id = None
|
self._join_id = None
|
||||||
@ -302,8 +302,9 @@ class Activity(Window, gtk.Container):
|
|||||||
share_scope = self._jobject.metadata['share-scope']
|
share_scope = self._jobject.metadata['share-scope']
|
||||||
|
|
||||||
# handle activity share/join
|
# handle activity share/join
|
||||||
mesh_instance = self._pservice.get_activity(self._activity_id,
|
pservice = presenceservice.get_instance()
|
||||||
warn_if_none=False)
|
mesh_instance = pservice.get_activity(self._activity_id,
|
||||||
|
warn_if_none=False)
|
||||||
logging.debug("*** Act %s, mesh instance %r, scope %s",
|
logging.debug("*** Act %s, mesh instance %r, scope %s",
|
||||||
self._activity_id, mesh_instance, share_scope)
|
self._activity_id, mesh_instance, share_scope)
|
||||||
if mesh_instance is not None:
|
if mesh_instance is not None:
|
||||||
|
@ -75,11 +75,14 @@ def create_activity_id():
|
|||||||
|
|
||||||
# check through network activities
|
# check through network activities
|
||||||
found = False
|
found = False
|
||||||
|
logging.info('KILL_PS check the activity_id is not used in the network')
|
||||||
|
"""
|
||||||
activities = pservice.get_activities()
|
activities = pservice.get_activities()
|
||||||
for act in activities:
|
for act in activities:
|
||||||
if act_id == act.props.id:
|
if act_id == act.props.id:
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
|
"""
|
||||||
if not found:
|
if not found:
|
||||||
return act_id
|
return act_id
|
||||||
raise RuntimeError("Cannot generate unique activity id.")
|
raise RuntimeError("Cannot generate unique activity id.")
|
||||||
|
@ -25,7 +25,14 @@ import logging
|
|||||||
import dbus
|
import dbus
|
||||||
import gobject
|
import gobject
|
||||||
import telepathy
|
import telepathy
|
||||||
|
from telepathy.client import Channel
|
||||||
|
from telepathy.interfaces import CHANNEL, \
|
||||||
|
CHANNEL_TYPE_TUBES, \
|
||||||
|
CHANNEL_TYPE_TEXT, \
|
||||||
|
CONNECTION
|
||||||
|
from telepathy.constants import HANDLE_TYPE_ROOM
|
||||||
|
|
||||||
|
CONN_INTERFACE_ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties'
|
||||||
|
|
||||||
_logger = logging.getLogger('sugar.presence.activity')
|
_logger = logging.getLogger('sugar.presence.activity')
|
||||||
|
|
||||||
@ -64,34 +71,14 @@ class Activity(gobject.GObject):
|
|||||||
'joined': (bool, None, None, False, gobject.PARAM_READABLE),
|
'joined': (bool, None, None, False, gobject.PARAM_READABLE),
|
||||||
}
|
}
|
||||||
|
|
||||||
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
|
def __init__(self, connection, room_handle):
|
||||||
_ACTIVITY_DBUS_INTERFACE = "org.laptop.Sugar.Presence.Activity"
|
|
||||||
|
|
||||||
def __init__(self, bus, new_obj_cb, del_obj_cb, object_path):
|
|
||||||
"""Initialse the activity interface, connecting to service"""
|
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
self.telepathy_room_handle = None
|
|
||||||
self._object_path = object_path
|
|
||||||
self._ps_new_object = new_obj_cb
|
|
||||||
self._ps_del_object = del_obj_cb
|
|
||||||
bobj = bus.get_object(self._PRESENCE_SERVICE, object_path)
|
|
||||||
self._activity = dbus.Interface(bobj, self._ACTIVITY_DBUS_INTERFACE)
|
|
||||||
self._activity.connect_to_signal('BuddyHandleJoined',
|
|
||||||
self._buddy_handle_joined_cb)
|
|
||||||
self._activity.connect_to_signal('BuddyLeft',
|
|
||||||
self._buddy_left_cb)
|
|
||||||
self._activity.connect_to_signal('NewChannel', self._new_channel_cb)
|
|
||||||
self._activity.connect_to_signal('PropertiesChanged',
|
|
||||||
self._properties_changed_cb,
|
|
||||||
utf8_strings=True)
|
|
||||||
# FIXME: this *would* just use a normal proxy call, but I want the
|
|
||||||
# pending call object so I can block on it, and normal proxy methods
|
|
||||||
# don't return those as of dbus-python 0.82.1; so do it the hard way
|
|
||||||
self._get_properties_call = bus.call_async(self._PRESENCE_SERVICE,
|
|
||||||
object_path, self._ACTIVITY_DBUS_INTERFACE, 'GetProperties',
|
|
||||||
'', (), self._get_properties_reply_cb,
|
|
||||||
self._get_properties_error_cb, utf8_strings=True)
|
|
||||||
|
|
||||||
|
self.telepathy_conn = connection
|
||||||
|
self.telepathy_text_chan = None
|
||||||
|
self.telepathy_tubes_chan = None
|
||||||
|
|
||||||
|
self._room_handle = room_handle
|
||||||
self._id = None
|
self._id = None
|
||||||
self._color = None
|
self._color = None
|
||||||
self._name = None
|
self._name = None
|
||||||
@ -99,31 +86,32 @@ class Activity(gobject.GObject):
|
|||||||
self._tags = None
|
self._tags = None
|
||||||
self._private = True
|
self._private = True
|
||||||
self._joined = False
|
self._joined = False
|
||||||
# Cache for get_buddy_by_handle, maps handles to buddy object paths
|
|
||||||
self._handle_to_buddy_path = {}
|
|
||||||
self._buddy_path_to_handle = {}
|
|
||||||
|
|
||||||
# Set up by set_up_tubes()
|
bus = dbus.SessionBus()
|
||||||
self.telepathy_conn = None
|
self._get_properties_call = bus.call_async(
|
||||||
self.telepathy_tubes_chan = None
|
connection.requested_bus_name,
|
||||||
self.telepathy_text_chan = None
|
connection.object_path,
|
||||||
self._telepathy_room = None
|
CONN_INTERFACE_ACTIVITY_PROPERTIES,
|
||||||
|
'GetProperties',
|
||||||
|
'u',
|
||||||
|
(self._room_handle,),
|
||||||
|
reply_handler=self._got_properties_cb,
|
||||||
|
error_handler=self._error_handler_cb,
|
||||||
|
utf8_strings=True)
|
||||||
|
|
||||||
def __repr__(self):
|
def _got_properties_cb(self, properties):
|
||||||
return ('<proxy for %s at %x>' % (self._object_path, id(self)))
|
_logger.debug('_got_properties_cb', properties)
|
||||||
|
|
||||||
def _get_properties_reply_cb(self, new_props):
|
|
||||||
self._get_properties_call = None
|
self._get_properties_call = None
|
||||||
_logger.debug('%r: initial GetProperties returned', self)
|
self._update_properties(properties)
|
||||||
self._properties_changed_cb(new_props)
|
|
||||||
|
|
||||||
def _get_properties_error_cb(self, e):
|
def _error_handler_cb(self, error):
|
||||||
self._get_properties_call = None
|
_logger.debug('_error_handler_cb', error)
|
||||||
# FIXME: do something with the error
|
|
||||||
_logger.warning('%r: Error doing initial GetProperties: %s', self, e)
|
|
||||||
|
|
||||||
def _properties_changed_cb(self, new_props):
|
def _properties_changed_cb(self, new_props):
|
||||||
_logger.debug('%r: Activity properties changed to %r', 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):
|
||||||
val = new_props.get('name', self._name)
|
val = new_props.get('name', self._name)
|
||||||
if isinstance(val, str) and val != self._name:
|
if isinstance(val, str) and val != self._name:
|
||||||
self._name = val
|
self._name = val
|
||||||
@ -244,16 +232,8 @@ class Activity(gobject.GObject):
|
|||||||
returns list of presence Buddy objects that we can successfully
|
returns list of presence Buddy objects that we can successfully
|
||||||
create from the buddy object paths that PS has for this activity.
|
create from the buddy object paths that PS has for this activity.
|
||||||
"""
|
"""
|
||||||
resp = self._activity.GetJoinedBuddies()
|
logging.info('KILL_PS return joined buddies')
|
||||||
buddies = []
|
return []
|
||||||
for item in resp:
|
|
||||||
try:
|
|
||||||
buddies.append(self._ps_new_object(item))
|
|
||||||
except dbus.DBusException:
|
|
||||||
_logger.debug(
|
|
||||||
'get_joined_buddies failed to get buddy object for %r',
|
|
||||||
item)
|
|
||||||
return buddies
|
|
||||||
|
|
||||||
def get_buddy_by_handle(self, handle):
|
def get_buddy_by_handle(self, handle):
|
||||||
"""Retrieve the Buddy object given a telepathy handle.
|
"""Retrieve the Buddy object given a telepathy handle.
|
||||||
@ -293,62 +273,38 @@ class Activity(gobject.GObject):
|
|||||||
_logger.debug('%r: finished setting up tubes', self)
|
_logger.debug('%r: finished setting up tubes', self)
|
||||||
reply_handler()
|
reply_handler()
|
||||||
|
|
||||||
def tubes_chan_ready(chan):
|
def tubes_channel_ready_cb(channel):
|
||||||
_logger.debug('%r: Tubes channel %r is ready', self, chan)
|
_logger.debug('%r: Tubes channel %r is ready', self, channel)
|
||||||
self.telepathy_tubes_chan = chan
|
self.telepathy_tubes_chan = channel
|
||||||
tubes_ready()
|
tubes_ready()
|
||||||
|
|
||||||
def text_chan_ready(chan):
|
def text_channel_ready_cb(channel):
|
||||||
_logger.debug('%r: Text channel %r is ready', self, chan)
|
_logger.debug('%r: Text channel %r is ready', self, channel)
|
||||||
self.telepathy_text_chan = chan
|
self.telepathy_text_chan = channel
|
||||||
tubes_ready()
|
tubes_ready()
|
||||||
|
|
||||||
def conn_ready(conn):
|
def create_text_channel_cb(channel_path):
|
||||||
_logger.debug('%r: Connection %r is ready', self, conn)
|
Channel(self.telepathy_conn.requested_bus_name, channel_path,
|
||||||
self.telepathy_conn = conn
|
ready_handler=text_channel_ready_cb)
|
||||||
found_text_channel = False
|
|
||||||
found_tubes_channel = False
|
|
||||||
|
|
||||||
for chan_path, chan_iface, handle_type, handle in chans:
|
def create_tubes_channel_cb(channel_path):
|
||||||
if handle_type != telepathy.HANDLE_TYPE_ROOM:
|
Channel(self.telepathy_conn.requested_bus_name, channel_path,
|
||||||
return
|
ready_handler=tubes_channel_ready_cb)
|
||||||
|
|
||||||
if chan_iface == telepathy.CHANNEL_TYPE_TEXT:
|
def error_handler_cb(error):
|
||||||
telepathy.client.Channel(
|
raise RuntimeError(error)
|
||||||
conn.service_name, chan_path,
|
|
||||||
ready_handler=text_chan_ready,
|
|
||||||
error_handler=error_handler)
|
|
||||||
found_text_channel = True
|
|
||||||
self.telepathy_room_handle = handle
|
|
||||||
|
|
||||||
elif chan_iface == telepathy.CHANNEL_TYPE_TUBES:
|
self.telepathy_conn.RequestChannel(CHANNEL_TYPE_TEXT,
|
||||||
telepathy.client.Channel(
|
HANDLE_TYPE_ROOM, self._room_handle, True,
|
||||||
conn.service_name, chan_path,
|
reply_handler=create_text_channel_cb,
|
||||||
ready_handler=tubes_chan_ready,
|
error_handler=error_handler_cb,
|
||||||
error_handler=error_handler)
|
dbus_interface=CONNECTION)
|
||||||
found_tubes_channel = True
|
|
||||||
|
|
||||||
if not found_text_channel:
|
self.telepathy_conn.RequestChannel(CHANNEL_TYPE_TUBES,
|
||||||
error_handler(AssertionError("Presence Service didn't create "
|
HANDLE_TYPE_ROOM, self._room_handle, True,
|
||||||
"a chatroom"))
|
reply_handler=create_tubes_channel_cb,
|
||||||
elif not found_tubes_channel:
|
error_handler=error_handler_cb,
|
||||||
error_handler(AssertionError("Presence Service didn't create "
|
dbus_interface=CONNECTION)
|
||||||
"tubes channel"))
|
|
||||||
|
|
||||||
def channels_listed(bus_name, conn_path, channels):
|
|
||||||
_logger.debug('%r: Connection on %s at %s, channels: %r',
|
|
||||||
self, bus_name, conn_path, channels)
|
|
||||||
|
|
||||||
# can't use assignment for this due to Python scoping
|
|
||||||
chans.extend(channels)
|
|
||||||
|
|
||||||
telepathy.client.Connection(bus_name, conn_path,
|
|
||||||
ready_handler=conn_ready,
|
|
||||||
error_handler=error_handler)
|
|
||||||
|
|
||||||
|
|
||||||
self._activity.ListChannels(reply_handler=channels_listed,
|
|
||||||
error_handler=error_handler)
|
|
||||||
|
|
||||||
def _join_cb(self):
|
def _join_cb(self):
|
||||||
_logger.debug('%r: Join finished', self)
|
_logger.debug('%r: Join finished', self)
|
||||||
@ -370,11 +326,7 @@ class Activity(gobject.GObject):
|
|||||||
|
|
||||||
_logger.debug('%r: joining', self)
|
_logger.debug('%r: joining', self)
|
||||||
|
|
||||||
def joined():
|
self.set_up_tubes(reply_handler=self._join_cb,
|
||||||
self.set_up_tubes(reply_handler=self._join_cb,
|
|
||||||
error_handler=self._join_error_cb)
|
|
||||||
|
|
||||||
self._activity.Join(reply_handler=joined,
|
|
||||||
error_handler=self._join_error_cb)
|
error_handler=self._join_error_cb)
|
||||||
|
|
||||||
# GetChannels() wrapper
|
# GetChannels() wrapper
|
||||||
|
@ -20,12 +20,21 @@
|
|||||||
STABLE.
|
STABLE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
import gobject
|
import gobject
|
||||||
import gtk
|
import gtk
|
||||||
import dbus
|
import dbus
|
||||||
|
import gconf
|
||||||
|
from telepathy.interfaces import CONNECTION_INTERFACE_ALIASING, \
|
||||||
|
CONNECTION_INTERFACE_CONTACTS
|
||||||
|
|
||||||
|
CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
|
||||||
|
|
||||||
|
_logger = logging.getLogger('sugar.presence.buddy')
|
||||||
|
|
||||||
|
|
||||||
class Buddy(gobject.GObject):
|
class BaseBuddy(gobject.GObject):
|
||||||
"""UI interface for a Buddy in the presence service
|
"""UI interface for a Buddy in the presence service
|
||||||
|
|
||||||
Each buddy interface tracks a set of activities and properties
|
Each buddy interface tracks a set of activities and properties
|
||||||
@ -42,6 +51,8 @@ class Buddy(gobject.GObject):
|
|||||||
See __gproperties__
|
See __gproperties__
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__gtype_name__ = 'PresenceBaseBuddy'
|
||||||
|
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
|
||||||
'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||||
@ -52,53 +63,17 @@ class Buddy(gobject.GObject):
|
|||||||
([gobject.TYPE_PYOBJECT])),
|
([gobject.TYPE_PYOBJECT])),
|
||||||
}
|
}
|
||||||
|
|
||||||
__gproperties__ = {
|
def __init__(self):
|
||||||
'key': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
'icon': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
'nick': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
'color': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
'current-activity': (object, None, None, gobject.PARAM_READABLE),
|
|
||||||
'owner': (bool, None, None, False, gobject.PARAM_READABLE),
|
|
||||||
'ip4-address': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
'tags': (str, None, None, None, gobject.PARAM_READABLE),
|
|
||||||
}
|
|
||||||
|
|
||||||
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
|
|
||||||
_BUDDY_DBUS_INTERFACE = "org.laptop.Sugar.Presence.Buddy"
|
|
||||||
|
|
||||||
def __init__(self, bus, new_obj_cb, del_obj_cb, object_path):
|
|
||||||
"""Initialise the reference to the buddy
|
|
||||||
|
|
||||||
bus -- dbus bus object
|
|
||||||
new_obj_cb -- callback to call when this buddy joins an activity
|
|
||||||
del_obj_cb -- callback to call when this buddy leaves an activity
|
|
||||||
object_path -- path to the buddy object
|
|
||||||
"""
|
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
self._object_path = object_path
|
|
||||||
self._ps_new_object = new_obj_cb
|
|
||||||
self._ps_del_object = del_obj_cb
|
|
||||||
self._properties = {}
|
|
||||||
self._activities = {}
|
|
||||||
|
|
||||||
bobj = bus.get_object(self._PRESENCE_SERVICE, object_path)
|
self._key = None
|
||||||
self._buddy = dbus.Interface(bobj, self._BUDDY_DBUS_INTERFACE)
|
|
||||||
|
|
||||||
self._icon_changed_signal = self._buddy.connect_to_signal(
|
|
||||||
'IconChanged', self._icon_changed_cb, byte_arrays=True)
|
|
||||||
self._joined_activity_signal = self._buddy.connect_to_signal(
|
|
||||||
'JoinedActivity', self._joined_activity_cb)
|
|
||||||
self._left_activity_signal = self._buddy.connect_to_signal(
|
|
||||||
'LeftActivity', self._left_activity_cb)
|
|
||||||
self._property_changed_signal = self._buddy.connect_to_signal(
|
|
||||||
'PropertyChanged', self._property_changed_cb)
|
|
||||||
|
|
||||||
self._properties = self._get_properties_helper()
|
|
||||||
|
|
||||||
activities = self._buddy.GetJoinedActivities()
|
|
||||||
for op in activities:
|
|
||||||
self._activities[op] = self._ps_new_object(op)
|
|
||||||
self._icon = None
|
self._icon = None
|
||||||
|
self._nick = None
|
||||||
|
self._color = None
|
||||||
|
self._current_activity = None
|
||||||
|
self._owner = False
|
||||||
|
self._ip4_address = None
|
||||||
|
self._tags = None
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
self._icon_changed_signal.remove()
|
self._icon_changed_signal.remove()
|
||||||
@ -114,41 +89,68 @@ class Buddy(gobject.GObject):
|
|||||||
return {}
|
return {}
|
||||||
return props
|
return props
|
||||||
|
|
||||||
def do_get_property(self, pspec):
|
def get_key(self):
|
||||||
"""Retrieve a particular property from our property dictionary
|
return self._key
|
||||||
|
|
||||||
pspec -- XXX some sort of GTK specifier object with attributes
|
def set_key(self, key):
|
||||||
including 'name', 'active' and 'icon-name'
|
self._key = key
|
||||||
"""
|
|
||||||
if pspec.name == "key":
|
key = gobject.property(type=str, getter=get_key, setter=set_key)
|
||||||
return self._properties["key"]
|
|
||||||
elif pspec.name == "nick":
|
def get_icon(self):
|
||||||
return self._properties["nick"]
|
raise NotImplementedError()
|
||||||
elif pspec.name == "color":
|
|
||||||
return self._properties["color"]
|
icon = gobject.property(type=str, getter=get_icon)
|
||||||
elif pspec.name == "tags":
|
|
||||||
return self._properties["tags"]
|
def get_nick(self):
|
||||||
elif pspec.name == "current-activity":
|
return self._nick
|
||||||
if not self._properties.has_key("current-activity"):
|
|
||||||
return None
|
def set_nick(self, nick):
|
||||||
curact = self._properties["current-activity"]
|
self._nick = nick
|
||||||
if not len(curact):
|
|
||||||
return None
|
nick = gobject.property(type=str, getter=get_nick, setter=set_nick)
|
||||||
for activity in self._activities.values():
|
|
||||||
if activity.props.id == curact:
|
def get_color(self):
|
||||||
return activity
|
return self._color
|
||||||
|
|
||||||
|
def set_color(self, color):
|
||||||
|
self._color = color
|
||||||
|
|
||||||
|
color = gobject.property(type=str, getter=get_color, setter=set_color)
|
||||||
|
|
||||||
|
def get_current_activity(self):
|
||||||
|
if self._current_activity is None:
|
||||||
return None
|
return None
|
||||||
elif pspec.name == "owner":
|
for activity in self._activities.values():
|
||||||
return self._properties["owner"]
|
if activity.props.id == self._current_activity:
|
||||||
elif pspec.name == "icon":
|
return activity
|
||||||
if not self._icon:
|
return None
|
||||||
self._icon = str(self._buddy.GetIcon(byte_arrays=True))
|
|
||||||
return self._icon
|
current_activity = gobject.property(type=object, getter=get_current_activity)
|
||||||
elif pspec.name == "ip4-address":
|
|
||||||
# IPv4 address will go away quite soon
|
def get_owner(self):
|
||||||
if not self._properties.has_key("ip4-address"):
|
return self._owner
|
||||||
return None
|
|
||||||
return self._properties["ip4-address"]
|
def set_owner(self, owner):
|
||||||
|
self._owner = owner
|
||||||
|
|
||||||
|
owner = gobject.property(type=bool, getter=get_owner, setter=set_owner, default=False)
|
||||||
|
|
||||||
|
def get_ip4_address(self):
|
||||||
|
return self._ip4_address
|
||||||
|
|
||||||
|
def set_ip4_address(self, ip4_address):
|
||||||
|
self._ip4_address = ip4_address
|
||||||
|
|
||||||
|
ip4_address = gobject.property(type=str, getter=get_ip4_address, setter=set_ip4_address)
|
||||||
|
|
||||||
|
def get_tags(self):
|
||||||
|
return self._tags
|
||||||
|
|
||||||
|
def set_tags(self, tags):
|
||||||
|
self._tags = tags
|
||||||
|
|
||||||
|
tags = gobject.property(type=str, getter=get_tags, setter=set_tags)
|
||||||
|
|
||||||
def object_path(self):
|
def object_path(self):
|
||||||
"""Retrieve our dbus object path"""
|
"""Retrieve our dbus object path"""
|
||||||
@ -160,16 +162,16 @@ class Buddy(gobject.GObject):
|
|||||||
self.emit('icon-changed')
|
self.emit('icon-changed')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _icon_changed_cb(self, icon_data):
|
def __icon_changed_cb(self, icon_data):
|
||||||
"""Handle dbus signal by emitting a GObject signal"""
|
"""Handle dbus signal by emitting a GObject signal"""
|
||||||
gobject.idle_add(self._emit_icon_changed_signal, icon_data)
|
gobject.idle_add(self._emit_icon_changed_signal, icon_data)
|
||||||
|
|
||||||
def _emit_joined_activity_signal(self, object_path):
|
def __emit_joined_activity_signal(self, object_path):
|
||||||
"""Emit activity joined signal with Activity object"""
|
"""Emit activity joined signal with Activity object"""
|
||||||
self.emit('joined-activity', self._ps_new_object(object_path))
|
self.emit('joined-activity', self._ps_new_object(object_path))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _joined_activity_cb(self, object_path):
|
def __joined_activity_cb(self, object_path):
|
||||||
"""Handle dbus signal by emitting a GObject signal
|
"""Handle dbus signal by emitting a GObject signal
|
||||||
|
|
||||||
Stores the activity in activities dictionary as well
|
Stores the activity in activities dictionary as well
|
||||||
@ -187,7 +189,7 @@ class Buddy(gobject.GObject):
|
|||||||
self.emit('left-activity', self._ps_new_object(object_path))
|
self.emit('left-activity', self._ps_new_object(object_path))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _left_activity_cb(self, object_path):
|
def __left_activity_cb(self, object_path):
|
||||||
"""Handle dbus signal by emitting a GObject signal
|
"""Handle dbus signal by emitting a GObject signal
|
||||||
|
|
||||||
Also removes from the activities dictionary
|
Also removes from the activities dictionary
|
||||||
@ -207,7 +209,7 @@ class Buddy(gobject.GObject):
|
|||||||
self.emit('property-changed', prop_list)
|
self.emit('property-changed', prop_list)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _property_changed_cb(self, prop_list):
|
def __property_changed_cb(self, prop_list):
|
||||||
"""Handle dbus signal by emitting a GObject signal"""
|
"""Handle dbus signal by emitting a GObject signal"""
|
||||||
gobject.idle_add(self._handle_property_changed_signal, prop_list)
|
gobject.idle_add(self._handle_property_changed_signal, prop_list)
|
||||||
|
|
||||||
@ -241,3 +243,92 @@ class Buddy(gobject.GObject):
|
|||||||
for item in resp:
|
for item in resp:
|
||||||
acts.append(self._ps_new_object(item))
|
acts.append(self._ps_new_object(item))
|
||||||
return acts
|
return acts
|
||||||
|
|
||||||
|
|
||||||
|
class Buddy(BaseBuddy):
|
||||||
|
__gtype_name__ = 'PresenceBuddy'
|
||||||
|
def __init__(self, connection, contact_handle):
|
||||||
|
BaseBuddy.__init__(self)
|
||||||
|
|
||||||
|
self._contact_handle = contact_handle
|
||||||
|
|
||||||
|
bus = dbus.SessionBus()
|
||||||
|
self._get_properties_call = bus.call_async(
|
||||||
|
connection.requested_bus_name,
|
||||||
|
connection.object_path,
|
||||||
|
CONN_INTERFACE_BUDDY_INFO,
|
||||||
|
'GetProperties',
|
||||||
|
'u',
|
||||||
|
(self._contact_handle,),
|
||||||
|
reply_handler=self.__got_properties_cb,
|
||||||
|
error_handler=self.__error_handler_cb,
|
||||||
|
utf8_strings=True,
|
||||||
|
byte_arrays=True)
|
||||||
|
|
||||||
|
self._get_attributes_call = bus.call_async(
|
||||||
|
connection.requested_bus_name,
|
||||||
|
connection.object_path,
|
||||||
|
CONNECTION_INTERFACE_CONTACTS,
|
||||||
|
'GetContactAttributes',
|
||||||
|
'auasb',
|
||||||
|
([self._contact_handle], [CONNECTION_INTERFACE_ALIASING], False),
|
||||||
|
reply_handler=self.__got_attributes_cb,
|
||||||
|
error_handler=self.__error_handler_cb)
|
||||||
|
|
||||||
|
def __got_properties_cb(self, properties):
|
||||||
|
_logger.debug('__got_properties_cb', properties)
|
||||||
|
self._get_properties_call = None
|
||||||
|
self._update_properties(properties)
|
||||||
|
|
||||||
|
def __got_attributes_cb(self, attributes):
|
||||||
|
_logger.debug('__got_attributes_cb', attributes)
|
||||||
|
self._get_attributes_call = None
|
||||||
|
self._update_attributes(attributes[self._contact_handle])
|
||||||
|
|
||||||
|
def __error_handler_cb(self, error):
|
||||||
|
_logger.debug('__error_handler_cb', error)
|
||||||
|
|
||||||
|
def __properties_changed_cb(self, new_props):
|
||||||
|
_logger.debug('%r: Buddy properties changed to %r', self, new_props)
|
||||||
|
self._update_properties(new_props)
|
||||||
|
|
||||||
|
def _update_properties(self, properties):
|
||||||
|
if 'key' in properties:
|
||||||
|
self.props.key = properties['key']
|
||||||
|
if 'icon' in properties:
|
||||||
|
self.props.icon = properties['icon']
|
||||||
|
if 'color' in properties:
|
||||||
|
self.props.color = properties['color']
|
||||||
|
if 'current-activity' in properties:
|
||||||
|
self.props.current_activity = properties['current-activity']
|
||||||
|
if 'owner' in properties:
|
||||||
|
self.props.owner = properties['owner']
|
||||||
|
if 'ip4-address' in properties:
|
||||||
|
self.props.ip4_address = properties['ip4-address']
|
||||||
|
if 'tags' in properties:
|
||||||
|
self.props.tags = properties['tags']
|
||||||
|
|
||||||
|
def _update_attributes(self, attributes):
|
||||||
|
nick_key = CONNECTION_INTERFACE_ALIASING + '/alias'
|
||||||
|
if nick_key in attributes:
|
||||||
|
self.props.nick = attributes[nick_key]
|
||||||
|
|
||||||
|
def do_get_property(self, pspec):
|
||||||
|
if self._get_properties_call is not None:
|
||||||
|
_logger.debug('%r: Blocking on GetProperties() because someone '
|
||||||
|
'wants property %s', self, pspec.name)
|
||||||
|
self._get_properties_call.block()
|
||||||
|
|
||||||
|
return BaseBuddy.do_get_property(self, pspec)
|
||||||
|
|
||||||
|
|
||||||
|
class Owner(BaseBuddy):
|
||||||
|
|
||||||
|
__gtype_name__ = 'PresenceOwner'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
BaseBuddy.__init__(self)
|
||||||
|
|
||||||
|
client = gconf.client_get_default()
|
||||||
|
self.props.nick = client.get_string("/desktop/sugar/user/nick")
|
||||||
|
self.props.color = client.get_string("/desktop/sugar/user/color")
|
||||||
|
@ -23,19 +23,16 @@ STABLE.
|
|||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
import gobject
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.exceptions
|
import dbus.exceptions
|
||||||
import dbus.glib
|
import dbus.glib
|
||||||
import gobject
|
|
||||||
|
|
||||||
from sugar.presence.buddy import Buddy
|
from sugar.presence.buddy import Buddy, Owner
|
||||||
from sugar.presence.activity import Activity
|
from sugar.presence.activity import Activity
|
||||||
|
from sugar.presence.util import get_connection_manager
|
||||||
|
|
||||||
|
|
||||||
DBUS_SERVICE = "org.laptop.Sugar.Presence"
|
|
||||||
DBUS_INTERFACE = "org.laptop.Sugar.Presence"
|
|
||||||
DBUS_PATH = "/org/laptop/Sugar/Presence"
|
|
||||||
|
|
||||||
_logger = logging.getLogger('sugar.presence.presenceservice')
|
_logger = logging.getLogger('sugar.presence.presenceservice')
|
||||||
|
|
||||||
|
|
||||||
@ -66,92 +63,12 @@ class PresenceService(gobject.GObject):
|
|||||||
gobject.TYPE_PYOBJECT])),
|
gobject.TYPE_PYOBJECT])),
|
||||||
}
|
}
|
||||||
|
|
||||||
_PS_BUDDY_OP = DBUS_PATH + "/Buddies/"
|
def __init__(self):
|
||||||
_PS_ACTIVITY_OP = DBUS_PATH + "/Activities/"
|
|
||||||
|
|
||||||
def __init__(self, allow_offline_iface=True):
|
|
||||||
"""Initialise the service and attempt to connect to events
|
"""Initialise the service and attempt to connect to events
|
||||||
"""
|
"""
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
self._objcache = {}
|
|
||||||
self._joined = None
|
|
||||||
|
|
||||||
# Get a connection to the session bus
|
self._buddy_cache = {}
|
||||||
self._bus = dbus.SessionBus()
|
|
||||||
self._bus.add_signal_receiver(self._name_owner_changed_cb,
|
|
||||||
signal_name="NameOwnerChanged",
|
|
||||||
dbus_interface="org.freedesktop.DBus")
|
|
||||||
|
|
||||||
# attempt to load the interface to the service...
|
|
||||||
self._allow_offline_iface = allow_offline_iface
|
|
||||||
self._get_ps()
|
|
||||||
|
|
||||||
def _name_owner_changed_cb(self, name, old, new):
|
|
||||||
if name != DBUS_SERVICE:
|
|
||||||
return
|
|
||||||
if (old and len(old)) and (not new and not len(new)):
|
|
||||||
# PS went away, clear out PS dbus service wrapper
|
|
||||||
self._ps_ = None
|
|
||||||
elif (not old and not len(old)) and (new and len(new)):
|
|
||||||
# PS started up
|
|
||||||
self._get_ps()
|
|
||||||
|
|
||||||
_ps_ = None
|
|
||||||
|
|
||||||
def _get_ps(self):
|
|
||||||
"""Retrieve dbus interface to PresenceService
|
|
||||||
|
|
||||||
Also registers for updates from various dbus events on the
|
|
||||||
interface.
|
|
||||||
|
|
||||||
If unable to retrieve the interface, we will temporarily
|
|
||||||
return an _OfflineInterface object to allow the calling
|
|
||||||
code to continue functioning as though it had accessed a
|
|
||||||
real presence service.
|
|
||||||
|
|
||||||
If successful, caches the presence service interface
|
|
||||||
for use by other methods and returns that interface
|
|
||||||
"""
|
|
||||||
if not self._ps_:
|
|
||||||
try:
|
|
||||||
# NOTE: We need to follow_name_owner_changes here
|
|
||||||
# because we can not connect to a signal unless
|
|
||||||
# we follow the changes or we start the service
|
|
||||||
# before we connect. Starting the service here
|
|
||||||
# causes a major bottleneck during startup
|
|
||||||
ps = dbus.Interface(
|
|
||||||
self._bus.get_object(DBUS_SERVICE,
|
|
||||||
DBUS_PATH,
|
|
||||||
follow_name_owner_changes=True),
|
|
||||||
DBUS_INTERFACE)
|
|
||||||
except dbus.exceptions.DBusException, err:
|
|
||||||
_logger.error(
|
|
||||||
"""Failure retrieving %r interface from
|
|
||||||
the D-BUS service %r %r: %s""",
|
|
||||||
DBUS_INTERFACE, DBUS_SERVICE, DBUS_PATH, err)
|
|
||||||
if self._allow_offline_iface:
|
|
||||||
return _OfflineInterface()
|
|
||||||
raise RuntimeError('Failed to connect to the presence '
|
|
||||||
'service.')
|
|
||||||
else:
|
|
||||||
self._ps_ = ps
|
|
||||||
ps.connect_to_signal('BuddyAppeared',
|
|
||||||
self._buddy_appeared_cb)
|
|
||||||
ps.connect_to_signal('BuddyDisappeared',
|
|
||||||
self._buddy_disappeared_cb)
|
|
||||||
ps.connect_to_signal('ActivityAppeared',
|
|
||||||
self._activity_appeared_cb)
|
|
||||||
ps.connect_to_signal('ActivityDisappeared',
|
|
||||||
self._activity_disappeared_cb)
|
|
||||||
ps.connect_to_signal('ActivityInvitation',
|
|
||||||
self._activity_invitation_cb)
|
|
||||||
ps.connect_to_signal('PrivateInvitation',
|
|
||||||
self._private_invitation_cb)
|
|
||||||
return self._ps_
|
|
||||||
|
|
||||||
_ps = property(_get_ps, None, None,
|
|
||||||
"""DBUS interface to the PresenceService
|
|
||||||
(services/presence/presenceservice)""")
|
|
||||||
|
|
||||||
def _new_object(self, object_path):
|
def _new_object(self, object_path):
|
||||||
"""Turn new object path into (cached) Buddy/Activity instance
|
"""Turn new object path into (cached) Buddy/Activity instance
|
||||||
@ -289,17 +206,11 @@ class PresenceService(gobject.GObject):
|
|||||||
returns list of Activity objects for all object paths
|
returns list of Activity objects for all object paths
|
||||||
the service reports exist (using GetActivities)
|
the service reports exist (using GetActivities)
|
||||||
"""
|
"""
|
||||||
try:
|
resp = self._ps.GetActivities()
|
||||||
resp = self._ps.GetActivities()
|
acts = []
|
||||||
except dbus.exceptions.DBusException:
|
for item in resp:
|
||||||
_logger.exception('Unable to retrieve activity list from '
|
acts.append(self._new_object(item))
|
||||||
'presence service')
|
return acts
|
||||||
return []
|
|
||||||
else:
|
|
||||||
acts = []
|
|
||||||
for item in resp:
|
|
||||||
acts.append(self._new_object(item))
|
|
||||||
return acts
|
|
||||||
|
|
||||||
def _get_activities_cb(self, reply_handler, resp):
|
def _get_activities_cb(self, reply_handler, resp):
|
||||||
acts = []
|
acts = []
|
||||||
@ -338,14 +249,14 @@ 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
|
||||||
"""
|
"""
|
||||||
try:
|
for connection in get_connection_manager().connections:
|
||||||
act_op = self._ps.GetActivityById(activity_id)
|
try:
|
||||||
except dbus.exceptions.DBusException, err:
|
room_handle = connection.GetActivity(activity_id)
|
||||||
if warn_if_none:
|
return Activity(connection, room_handle)
|
||||||
_logger.warn("Unable to retrieve activity handle for %r from "
|
except:
|
||||||
"presence service: %s", activity_id, err)
|
pass
|
||||||
return None
|
|
||||||
return self._new_object(act_op)
|
return None
|
||||||
|
|
||||||
def get_buddies(self):
|
def get_buddies(self):
|
||||||
"""Retrieve set of all buddies from service
|
"""Retrieve set of all buddies from service
|
||||||
@ -426,26 +337,19 @@ class PresenceService(gobject.GObject):
|
|||||||
channel-specific handle.
|
channel-specific handle.
|
||||||
:Returns: the Buddy object, or None if the buddy is not found
|
:Returns: the Buddy object, or None if the buddy is not found
|
||||||
"""
|
"""
|
||||||
try:
|
logging.info('KILL_PS decide how to invalidate this cache')
|
||||||
buddy_op = self._ps.GetBuddyByTelepathyHandle(tp_conn_name,
|
if (tp_conn_path, handle) in self._buddy_cache:
|
||||||
tp_conn_path,
|
return self._buddy_cache[(tp_conn_path, handle)]
|
||||||
handle)
|
else:
|
||||||
except dbus.exceptions.DBusException, err:
|
bus = dbus.SessionBus()
|
||||||
_logger.warn('Unable to retrieve buddy handle for handle %u at '
|
connection = bus.get_object(tp_conn_name, tp_conn_path)
|
||||||
'conn %s:%s from presence service: %s',
|
buddy = Buddy(connection, handle)
|
||||||
handle, tp_conn_name, tp_conn_path, err)
|
self._buddy_cache[(tp_conn_path, handle)] = buddy
|
||||||
return None
|
return buddy
|
||||||
return self._new_object(buddy_op)
|
|
||||||
|
|
||||||
def get_owner(self):
|
def get_owner(self):
|
||||||
"""Retrieves the laptop "owner" Buddy object."""
|
"""Retrieves the laptop Buddy object."""
|
||||||
try:
|
return Owner()
|
||||||
owner_op = self._ps.GetOwner()
|
|
||||||
except dbus.exceptions.DBusException:
|
|
||||||
_logger.exception('Unable to retrieve local user/owner from '
|
|
||||||
'presence service')
|
|
||||||
raise RuntimeError("Could not get owner object.")
|
|
||||||
return self._new_object(owner_op)
|
|
||||||
|
|
||||||
def _share_activity_cb(self, activity, op):
|
def _share_activity_cb(self, activity, op):
|
||||||
"""Finish sharing the activity
|
"""Finish sharing the activity
|
||||||
@ -505,14 +409,11 @@ class PresenceService(gobject.GObject):
|
|||||||
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()
|
||||||
try:
|
if connection is None:
|
||||||
bus_name, object_path = self._ps.GetPreferredConnection()
|
|
||||||
except dbus.exceptions.DBusException:
|
|
||||||
logging.error(traceback.format_exc())
|
|
||||||
return None
|
return None
|
||||||
|
else:
|
||||||
return bus_name, object_path
|
return connection.requested_bus_name, connection.object_path
|
||||||
|
|
||||||
|
|
||||||
class _OfflineInterface(object):
|
class _OfflineInterface(object):
|
||||||
@ -596,5 +497,5 @@ def get_instance(allow_offline_iface=False):
|
|||||||
"""Retrieve this process' view of the PresenceService"""
|
"""Retrieve this process' view of the PresenceService"""
|
||||||
global _ps
|
global _ps
|
||||||
if not _ps:
|
if not _ps:
|
||||||
_ps = PresenceService(allow_offline_iface)
|
_ps = PresenceService()
|
||||||
return _ps
|
return _ps
|
||||||
|
Loading…
Reference in New Issue
Block a user