diff --git a/services/presence/Buddy.py b/services/presence/Buddy.py index 5d13c29d..bf369f7e 100644 --- a/services/presence/Buddy.py +++ b/services/presence/Buddy.py @@ -10,6 +10,9 @@ PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp" BUDDY_DBUS_OBJECT_PATH = "/org/laptop/Presence/Buddies/" BUDDY_DBUS_INTERFACE = "org.laptop.Presence.Buddy" +_BUDDY_KEY_COLOR = 'color' +_BUDDY_KEY_CURACT = 'curact' + class NotFoundError(Exception): pass @@ -85,7 +88,10 @@ class BuddyDBusHelper(dbus.service.Object): props['owner'] = self._parent.is_owner() color = self._parent.get_color() if color: - props['color'] = self._parent.get_color() + props[_BUDDY_KEY_COLOR] = self._parent.get_color() + curact = self._parent.get_current_activity() + if curact: + props[_BUDDY_KEY_CURACT] = self._parent.get_current_activity() return props @@ -113,6 +119,7 @@ class Buddy(object): self._nick_name = service.get_name() self._address = service.get_source_address() self._color = None + self._current_activity = None self._valid = False self._icon = None self._icon_tries = 0 @@ -121,6 +128,7 @@ class Buddy(object): self._object_path = BUDDY_DBUS_OBJECT_PATH + str(self._object_id) self._dbus_helper = BuddyDBusHelper(self, bus_name, self._object_path) + self._buddy_presence_service = None if service is not None: self.add_service(service) @@ -181,25 +189,48 @@ class Buddy(object): service_key[1])) return False + if service.get_type() == PRESENCE_SERVICE_TYPE and self._buddy_presence_service: + # already have a presence service for this buddy + logging.debug("!!! Tried to add a buddy presence service when " \ + "one already existed.") + return False + logging.debug("Buddy %s added service type %s id %s" % (self._nick_name, service.get_type(), service.get_activity_id())) self._services[service_key] = service service.set_owner(self) if service.get_type() == PRESENCE_SERVICE_TYPE: + self._buddy_presence_service = service # A buddy isn't valid until its official presence # service has been found and resolved self._valid = True - print 'Requesting buddy icon %s' % self._nick_name + logging.debug('Requesting buddy icon %s' % self._nick_name) self._request_buddy_icon(service) - self._color = service.get_one_property('color') + self._color = service.get_one_property(_BUDDY_KEY_COLOR) if self._color: - self._dbus_helper.PropertyChanged(['color']) + self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) + # Monitor further buddy property changes, like current activity + # and color + service.connect('property-changed', + self.__buddy_presence_service_property_changed_cb) if self._valid: self._dbus_helper.ServiceAppeared(service.object_path()) return True + def __buddy_presence_service_property_changed_cb(self, service, keys): + if _BUDDY_KEY_COLOR in keys: + new_color = service.get_one_property(_BUDDY_KEY_COLOR) + if new_color and self._color != new_color: + self._color = new_color + self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) + if _BUDDY_KEY_CURACT in keys: + new_curact = service.get_one_property(_BUDDY_KEY_CURACT) + if new_curact and self._current_activity != new_curact: + self._current_activity = new_curact + self._dbus_helper.PropertyChanged([_BUDDY_KEY_CURACT]) + def add_activity(self, activity): actid = activity.get_id() if activity in self._activities.values(): @@ -222,6 +253,13 @@ class Buddy(object): return if service.get_name() != self._nick_name: return + + if service.get_type() == PRESENCE_SERVICE_TYPE \ + and self._buddy_presence_service \ + and service != self._buddy_presence_service: + logging.debug("!!! Tried to remove a spurious buddy presence service.") + return + service_key = self._get_service_key(service) if self._services.has_key(service_key): if self._valid: @@ -282,6 +320,9 @@ class Buddy(object): def get_color(self): return self._color + def get_current_activity(self): + return self._current_activity + def _set_icon(self, icon): """Can only set icon for other buddies. The Owner takes care of setting it's own icon.""" diff --git a/services/presence/Service.py b/services/presence/Service.py index 1b29f988..cc49e283 100644 --- a/services/presence/Service.py +++ b/services/presence/Service.py @@ -5,6 +5,7 @@ from sugar import util import dbus, dbus.service import random import logging +import gobject def compose_service_name(name, activity_id): if type(name) == type(""): @@ -135,13 +136,19 @@ class ServiceDBusHelper(dbus.service.Object): self._parent.set_properties(values, sender) -class Service(object): +class Service(gobject.GObject): """Encapsulates information about a specific ZeroConf/mDNS service as advertised on the network.""" + __gsignals__ = { + 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + def __init__(self, bus_name, object_id, name, stype, domain=u"local", address=None, port=-1, properties=None, source_address=None, local_publisher=None): + gobject.GObject.__init__(self) if not bus_name: raise ValueError("DBus bus name must be valid") if not object_id or type(object_id) != type(1): @@ -178,7 +185,7 @@ class Service(object): self._domain = domain self._port = -1 self.set_port(port) - self._properties = None + self._properties = {} self._dbus_helper = None self._internal_set_properties(properties) @@ -252,6 +259,11 @@ class Service(object): properties.""" return self._properties + def __emit_properties_changed_signal(self, keys): + if self._dbus_helper: + self._dbus_helper.PublishedValueChanged(keys) + self.emit('property-changed', keys) + def set_property(self, key, value, sender=None): """Set one service property""" if not self._local_publisher: @@ -290,8 +302,7 @@ class Service(object): if self._local_publisher and self._avahi_entry_group: self.__internal_update_avahi_properties() - if self._dbus_helper: - self._dbus_helper.PublishedValueChanged([key]) + self.__emit_properties_changed_signal([key]) def set_properties(self, properties, sender=None, from_network=False): """Set all service properties in one call""" @@ -306,13 +317,13 @@ class Service(object): python dictionary.""" if type(properties) != type({}): raise ValueError("Properties must be a dictionary.") - self._properties = {} # Make sure the properties are actually different diff_keys = _dicts_differ(self._properties, properties) if len(diff_keys) == 0: return + self._properties = {} # Set key/value pairs on internal property list for key, value in properties.items(): if len(key) == 0: @@ -329,8 +340,7 @@ class Service(object): if self._local_publisher and self._avahi_entry_group and not from_network: self.__internal_update_avahi_properties() - if self._dbus_helper: - self._dbus_helper.PublishedValueChanged(diff_keys) + self.__emit_properties_changed_signal(diff_keys) def __internal_update_avahi_properties(self): info = _convert_properties_to_dbus_byte_array(self._properties) diff --git a/shell/model/BuddyModel.py b/shell/model/BuddyModel.py index 0d36a4e2..9e6c6bd3 100644 --- a/shell/model/BuddyModel.py +++ b/shell/model/BuddyModel.py @@ -3,13 +3,19 @@ from sugar.canvas.IconColor import IconColor class BuddyModel: def __init__(self, buddy=None): - if buddy: - self.set_name(buddy.get_name()) - self.set_color(buddy.get_color()) - self._buddy = buddy self._cur_activity = None self._pservice = PresenceService.get_instance() - self._pservice.connect('buddy-appeared', self.__buddy_appeared_cb) + + self._buddy = buddy + if self._buddy: + self.set_name(self._buddy.get_name()) + self.set_color(self._buddy.get_color()) + self._buddy.connect('property-changed', + self.__buddy_property_changed_cb) + else: + # if we don't have a buddy yet, connect to the PS + # and wait until the buddy pops up on the network + self._pservice.connect('buddy-appeared', self.__buddy_appeared_cb) def set_name(self, name): self._name = name @@ -24,18 +30,24 @@ class BuddyModel: return self._color def get_buddy(self): - if not self._buddy: - self._buddy = self._pservice.get_buddy_by_name(self._name) - if self._buddy: - self._buddy.connect('property-changed', - self.__buddy_property_changed_cb) + # If we have a buddy already, just return + if self._buddy: + return self._buddy + + # Otherwise try to get the buddy from the PS + self._buddy = self._pservice.get_buddy_by_name(self._name) + if self._buddy: + self._buddy.connect('property-changed', + self.__buddy_property_changed_cb) return self._buddy def __buddy_appeared_cb(self, pservice, buddy): + # FIXME: use public key rather than buddy name if not self._buddy and buddy.get_name() == self._name: self.get_buddy() def __buddy_property_changed_cb(self, buddy, keys): + # all we care about right now is current activity curact = self._buddy.get_current_activity() self._cur_activity = self._pservice.get_activity(curact) diff --git a/sugar/presence/Buddy.py b/sugar/presence/Buddy.py index da664b6d..ee7bfdef 100644 --- a/sugar/presence/Buddy.py +++ b/sugar/presence/Buddy.py @@ -36,7 +36,13 @@ class Buddy(gobject.GObject): self._buddy.connect_to_signal('JoinedActivity', self._joined_activity_cb) self._buddy.connect_to_signal('LeftActivity', self._left_activity_cb) self._buddy.connect_to_signal('PropertyChanged', self._property_changed_cb) - self._properties = self._buddy.getProperties() + self._properties = self._get_properties_helper() + + def _get_properties_helper(self): + props = self._buddy.getProperties() + if not props: + return {} + return props def object_path(self): return self._object_path @@ -77,7 +83,7 @@ class Buddy(gobject.GObject): gobject.idle_add(self._emit_left_activity_signal, object_path) def _handle_property_changed_signal(self, prop_list): - self._properties = self._buddy.getProperties() + self._properties = self._get_properties_helper() self.emit('property-changed', prop_list) return False