diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-06-08 14:34:20 (GMT) |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-06-08 14:34:20 (GMT) |
commit | ac53c1c84cb5bba4db2587b71000b69183334843 (patch) | |
tree | ced2174e6a4e762a73ed79b1b1bffb6e9eb1b83b | |
parent | 5741a079edae63de0c2a06b9bd037dc5f68cedf2 (diff) |
Make GenericOwner responsible for setting its own properties.
* buddyiconcache: Use a single global instance, buddy_icon_cache
(which is imported into server_plugin and buddy)
* server_plugin, buddy: Move alias, avatar, props setting from ServerPlugin to
GenericOwner. Remove property-changed signal from Buddy (no longer needed)
Note that in this patch, GenericOwner temporarily makes calls to private
methods of ServerPlugin.
-rw-r--r-- | src/buddy.py | 154 | ||||
-rw-r--r-- | src/buddyiconcache.py | 2 | ||||
-rw-r--r-- | src/presenceservice.py | 6 | ||||
-rw-r--r-- | src/server_plugin.py | 150 |
4 files changed, 161 insertions, 151 deletions
diff --git a/src/buddy.py b/src/buddy.py index 68b9ac0..b550fc0 100644 --- a/src/buddy.py +++ b/src/buddy.py @@ -17,14 +17,29 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os +import logging +try: + # Python >= 2.5 + from hashlib import md5 as new_md5 +except ImportError: + from md5 import new as new_md5 + import gobject +import gtk import dbus import dbus.service from dbus.gobject_service import ExportedGObject -import psutils +from telepathy.constants import CONNECTION_STATUS_CONNECTED +from telepathy.interfaces import (CONN_INTERFACE_ALIASING, + CONN_INTERFACE_AVATARS) from sugar import env, profile -import logging + +import psutils +from buddyiconcache import buddy_icon_cache + + +CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' _BUDDY_PATH = "/org/laptop/Sugar/Presence/Buddies/" _BUDDY_INTERFACE = "org.laptop.Sugar.Presence.Buddy" @@ -44,6 +59,41 @@ _PROP_IP4_ADDRESS = "ip4-address" _logger = logging.getLogger('s-p-s.buddy') +def _noop(*args, **kwargs): + pass + +def _buddy_icon_save_cb(buf, data): + data[0] += buf + return True + +def _get_buddy_icon_at_size(icon, maxw, maxh, maxsize): + loader = gtk.gdk.PixbufLoader() + loader.write(icon) + loader.close() + unscaled_pixbuf = loader.get_pixbuf() + del loader + + pixbuf = unscaled_pixbuf.scale_simple(maxw, maxh, gtk.gdk.INTERP_BILINEAR) + del unscaled_pixbuf + + data = [""] + quality = 90 + img_size = maxsize + 1 + while img_size > maxsize: + data = [""] + pixbuf.save_to_callback(_buddy_icon_save_cb, "jpeg", + {"quality":"%d" % quality}, data) + quality -= 10 + img_size = len(data[0]) + del pixbuf + + if img_size > maxsize: + data = [""] + raise RuntimeError("could not size image less than %d bytes" % maxsize) + + return str(data[0]) + + class Buddy(ExportedGObject): """Person on the network (tracks properties and shared activites) @@ -190,7 +240,6 @@ class Buddy(ExportedGObject): if str(value) != self._icon: self._icon = str(value) self.IconChanged(self._icon) - self.emit('icon-changed', self._icon) elif pspec.name == _PROP_NICK: self._nick = value elif pspec.name == _PROP_COLOR: @@ -468,10 +517,13 @@ class Buddy(ExportedGObject): dbus_changed[key] = "" self.PropertyChanged(dbus_changed) - self.emit('property-changed', changed_props) + self._property_changed(changed_props) self._update_validity() + def _property_changed(self, changed_props): + pass + def _update_validity(self): """Check whether we are now valid @@ -526,7 +578,99 @@ class GenericOwner(Buddy): Buddy.__init__(self, bus, object_id, **kwargs) self._owner = True - self._bus = dbus.SessionBus() + self._bus = bus + + def _set_self_alias(self, tp): + self_handle = self.handles[tp] + conn = tp.get_connection() + conn[CONN_INTERFACE_ALIASING].SetAliases({self_handle: self._nick}, + reply_handler=_noop, + error_handler=lambda e: + _logger.warning('Error setting alias: %s', e)) + # Hack so we can use this as a timeout handler + return False + + def _set_self_olpc_properties(self, tp): + conn = tp.get_connection() + # FIXME: omit color/key/ip4-address if None? + conn[CONN_INTERFACE_BUDDY_INFO].SetProperties( + {'color': self._color or '', 'key': self._key or '', + 'ip4-address': self._ip4_address or '' }, + reply_handler=_noop, + error_handler=lambda e: + _logger.warning('Error setting alias: %s', e)) + # Hack so we can use this as a timeout handler + return False + + def add_telepathy_handle(self, tp_client, handle): + Buddy.add_telepathy_handle(self, tp_client, handle) + + self._set_self_olpc_properties(tp_client) + self._set_self_alias(tp_client) + # Hack; send twice to make sure the server gets it + gobject.timeout_add(1000, lambda: self._set_self_alias(tp_client)) + + # FIXME: using private API, for now + tp_client._set_self_activities() + tp_client._set_self_current_activity() + + self._set_self_avatar(tp_client) + + def IconChanged(self, icon_data): + # As well as emitting the D-Bus signal, prod the Telepathy + # connection manager + Buddy.IconChanged(self, icon_data) + for tp in self.handles.iterkeys(): + self._set_self_avatar(tp) + + def _set_self_avatar(self, tp): + conn = tp.get_connection() + icon_data = self._icon + + m = new_md5() + m.update(icon_data) + digest = m.hexdigest() + + self_handle = self.handles[tp] + token = conn[CONN_INTERFACE_AVATARS].GetAvatarTokens( + [self_handle])[0] + + if buddy_icon_cache.check_avatar(conn.object_path, digest, + token): + # avatar is up to date + return + + def set_self_avatar_cb(token): + buddy_icon_cache.set_avatar(conn.object_path, digest, token) + + types, minw, minh, maxw, maxh, maxsize = \ + conn[CONN_INTERFACE_AVATARS].GetAvatarRequirements() + if not "image/jpeg" in types: + _logger.debug("server does not accept JPEG format avatars.") + return + + img_data = _get_buddy_icon_at_size(icon_data, min(maxw, 96), + min(maxh, 96), maxsize) + conn[CONN_INTERFACE_AVATARS].SetAvatar(img_data, "image/jpeg", + reply_handler=set_self_avatar_cb, + error_handler=lambda e: + _logger.warning('Error setting avatar: %s', e)) + + def _property_changed(self, changed_props): + for tp in self.handles.iterkeys(): + + if changed_props.has_key("current-activity"): + tp._set_self_current_activity() + + if changed_props.has_key("nick"): + self._set_self_alias(tp) + # Hack; send twice to make sure the server gets it + gobject.timeout_add(1000, lambda: self._set_self_alias(tp)) + + if (changed_props.has_key("color") or + changed_props.has_key("ip4-address")): + if tp.status == CONNECTION_STATUS_CONNECTED: + self._set_self_olpc_properties(tp) def _ip4_address_changed_cb(self, monitor, address): """Handle IPv4 address change, set property to generate event""" diff --git a/src/buddyiconcache.py b/src/buddyiconcache.py index b7da614..b95ea98 100644 --- a/src/buddyiconcache.py +++ b/src/buddyiconcache.py @@ -81,6 +81,8 @@ class BuddyIconCache(object): self._token = token self._save_cache() +buddy_icon_cache = BuddyIconCache() + if __name__ == "__main__": my_cache = BuddyIconCache() diff --git a/src/presenceservice.py b/src/presenceservice.py index 8aeaac6..32dc5a9 100644 --- a/src/presenceservice.py +++ b/src/presenceservice.py @@ -35,7 +35,6 @@ from sugar import util from server_plugin import ServerPlugin from linklocal_plugin import LinkLocalPlugin from buddy import Buddy, ShellOwner -from buddyiconcache import BuddyIconCache from activity import Activity from psutils import pubkey_to_keyid @@ -68,8 +67,6 @@ class PresenceService(ExportedGObject): self._next_object_id = 0 self._connected = False - self._icon_cache = BuddyIconCache() - # all Buddy objects # identifier -> Buddy, GC'd when no more refs exist self._buddies = WeakValueDictionary() @@ -101,8 +98,7 @@ class PresenceService(ExportedGObject): self._registry.LoadManagers() # Set up the server connection - self._server_plugin = ServerPlugin(self._registry, self._owner, - self._icon_cache) + self._server_plugin = ServerPlugin(self._registry, self._owner) self._handles_buddies[self._server_plugin] = {} self._server_plugin.connect('status', self._server_status_cb) diff --git a/src/server_plugin.py b/src/server_plugin.py index 160e9fa..4a9d947 100644 --- a/src/server_plugin.py +++ b/src/server_plugin.py @@ -21,11 +21,6 @@ import logging import os import sys from string import hexdigits -try: - # Python >= 2.5 - from hashlib import md5 -except ImportError: - from md5 import new as md5 # Other libraries import dbus @@ -49,6 +44,7 @@ from sugar import util # Presence Service local modules import psutils +from buddyiconcache import buddy_icon_cache CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' @@ -61,37 +57,6 @@ _logger = logging.getLogger('s-p-s.server_plugin') _RECONNECT_TIMEOUT = 5000 -def _buddy_icon_save_cb(buf, data): - data[0] += buf - return True - -def _get_buddy_icon_at_size(icon, maxw, maxh, maxsize): - loader = gtk.gdk.PixbufLoader() - loader.write(icon) - loader.close() - unscaled_pixbuf = loader.get_pixbuf() - del loader - - pixbuf = unscaled_pixbuf.scale_simple(maxw, maxh, gtk.gdk.INTERP_BILINEAR) - del unscaled_pixbuf - - data = [""] - quality = 90 - img_size = maxsize + 1 - while img_size > maxsize: - data = [""] - pixbuf.save_to_callback(_buddy_icon_save_cb, "jpeg", - {"quality":"%d" % quality}, data) - quality -= 10 - img_size = len(data[0]) - del pixbuf - - if img_size > maxsize: - data = [""] - raise RuntimeError("could not size image less than %d bytes" % maxsize) - - return str(data[0]) - class ServerPlugin(gobject.GObject): """Telepathy-python-based presence server interface @@ -159,7 +124,7 @@ class ServerPlugin(gobject.GObject): (gobject.SIGNAL_RUN_FIRST, None, [object, object, object]), } - def __init__(self, registry, owner, icon_cache): + def __init__(self, registry, owner): """Initialize the ServerPlugin instance registry -- telepathy.client.ManagerRegistry from the @@ -171,7 +136,6 @@ class ServerPlugin(gobject.GObject): gobject.GObject.__init__(self) self._conn = None - self._icon_cache = icon_cache self._registry = registry self._online_contacts = {} # handle -> jid @@ -182,9 +146,6 @@ class ServerPlugin(gobject.GObject): self._joined_activities = [] self._owner = owner - self._owner.connect("property-changed", - self._owner_property_changed_cb) - self._owner.connect("icon-changed", self._owner_icon_changed_cb) self.self_handle = None self._account = self._get_account_info() @@ -201,6 +162,11 @@ class ServerPlugin(gobject.GObject): self._subscribe_local_pending = set() self._subscribe_remote_pending = set() + @property + def status(self): + """Return the Telepathy connection status.""" + return self._conn_status + def _ip4_address_changed_cb(self, ip4am, address): _logger.debug("::: IP4 address now %s", address) if address: @@ -213,40 +179,6 @@ class ServerPlugin(gobject.GObject): _logger.debug("::: invalid IP4 address, will disconnect") self.cleanup() - def _owner_property_changed_cb(self, owner, properties): - """Local user's configuration properties have changed - - owner -- the Buddy object for the local user - properties -- set of updated properties - - calls: - - _set_self_current_activity current-activity - _set_self_alias nick - _set_self_olpc_properties color - - depending on which properties are present in the - set of properties. - """ - _logger.debug("Owner properties changed: %s", properties) - - if properties.has_key("current-activity"): - self._set_self_current_activity() - - if properties.has_key("nick"): - self._set_self_alias() - # Hack; send twice to make sure the server gets it - gobject.timeout_add(1000, self._set_self_alias) - - if properties.has_key("color") or properties.has_key("ip4-address"): - if self._conn_status == CONNECTION_STATUS_CONNECTED: - self._set_self_olpc_properties() - - def _owner_icon_changed_cb(self, owner, icon): - """Owner has changed their icon, forward to network""" - _logger.debug("Owner icon changed to size %d", len(str(icon))) - self._set_self_avatar(icon) - def _get_account_info(self): """Retrieve metadata dictionary describing this account @@ -423,51 +355,10 @@ class ServerPlugin(gobject.GObject): 'ActivityPropertiesChanged', self._activity_properties_changed_cb) - # Set initial buddy properties, avatar, and activities - self._set_self_olpc_properties() - self._set_self_alias() - # Hack; send twice to make sure the server gets it - gobject.timeout_add(1000, self._set_self_alias) - self._set_self_activities() - self._set_self_current_activity() - self._set_self_avatar() - # Request presence for everyone we're subscribed to self._conn[CONN_INTERFACE_PRESENCE].RequestPresence(subscribe_handles) return True - def _set_self_avatar(self, icon_data=None): - if not icon_data: - icon_data = self._owner.props.icon - - m = md5() - m.update(icon_data) - digest = m.hexdigest() - - self_handle = self._conn[CONN_INTERFACE].GetSelfHandle() - token = self._conn[CONN_INTERFACE_AVATARS].GetAvatarTokens( - [self_handle])[0] - - if self._icon_cache.check_avatar(self._conn.object_path, digest, - token): - # avatar is up to date - return - - def set_self_avatar_cb(token): - self._icon_cache.set_avatar(self._conn.object_path, digest, token) - - types, minw, minh, maxw, maxh, maxsize = \ - self._conn[CONN_INTERFACE_AVATARS].GetAvatarRequirements() - if not "image/jpeg" in types: - _logger.debug("server does not accept JPEG format avatars.") - return - - img_data = _get_buddy_icon_at_size(icon_data, min(maxw, 96), - min(maxh, 96), maxsize) - self._conn[CONN_INTERFACE_AVATARS].SetAvatar(img_data, "image/jpeg", - reply_handler=set_self_avatar_cb, - error_handler=lambda e: self._log_error_cb("setting avatar", e)) - def emit_joined_activity(self, activity_id, room): self._joined_activities.append((activity_id, room)) self._set_self_activities() @@ -485,29 +376,6 @@ class ServerPlugin(gobject.GObject): """Log a message (error) at debug level with prefix msg""" _logger.debug("Error %s: %s", msg, err) - def _set_self_olpc_properties(self): - """Set color and key on our Telepathy server identity""" - props = {} - props['color'] = self._owner.props.color - props['key'] = dbus.ByteArray(self._owner.props.key) - addr = self._owner.props.ip4_address - if not addr: - props['ip4-address'] = "" - else: - props['ip4-address'] = addr - self._conn[CONN_INTERFACE_BUDDY_INFO].SetProperties(props, - reply_handler=self._ignore_success_cb, - error_handler=lambda e: self._log_error_cb("setting properties", e)) - - def _set_self_alias(self): - """Forwarded to SetActivities on AliasInfo channel""" - alias = self._owner.props.nick - self_handle = self._conn[CONN_INTERFACE].GetSelfHandle() - self._conn[CONN_INTERFACE_ALIASING].SetAliases({self_handle : alias}, - reply_handler=self._ignore_success_cb, - error_handler=lambda e: self._log_error_cb("setting alias", e)) - return False - def _set_self_activities(self): """Forward set of joined activities to network @@ -815,7 +683,7 @@ class ServerPlugin(gobject.GObject): logging.debug("Handle %s not valid yet..." % handle) return icon = ''.join(map(chr, avatar)) - self._icon_cache.store_icon(self._conn.object_path, jid, + buddy_icon_cache.store_icon(self._conn.object_path, jid, new_avatar_token, icon) self.emit("avatar-updated", handle, icon) @@ -835,7 +703,7 @@ class ServerPlugin(gobject.GObject): _logger.debug("Handle %s not valid yet...", handle) return - icon = self._icon_cache.get_icon(self._conn.object_path, jid, + icon = buddy_icon_cache.get_icon(self._conn.object_path, jid, new_avatar_token) if not icon: # cache miss |