Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon 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)
commitac53c1c84cb5bba4db2587b71000b69183334843 (patch)
treeced2174e6a4e762a73ed79b1b1bffb6e9eb1b83b
parent5741a079edae63de0c2a06b9bd037dc5f68cedf2 (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.py154
-rw-r--r--src/buddyiconcache.py2
-rw-r--r--src/presenceservice.py6
-rw-r--r--src/server_plugin.py150
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