new icon cache system

This commit is contained in:
Guillaume Desmottes 2007-02-26 17:18:52 +01:00
parent a1f5684944
commit 4c0352e9bc
4 changed files with 89 additions and 68 deletions

View File

@ -32,7 +32,7 @@ class Buddy(dbus.service.Object):
"""Represents another person on the network and keeps track of the
activities and resources they make available for sharing."""
def __init__(self, bus_name, object_id, icon_cache, handle=None):
def __init__(self, bus_name, object_id, handle=None):
if not bus_name:
raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int):
@ -46,10 +46,9 @@ class Buddy(dbus.service.Object):
self._activities = {} # Activity ID -> Activity
self._icon_cache = icon_cache
self.handles = {} # tp client -> handle
self._icon = None
self._nick_name = None
self._color = None
self._key = None
@ -149,7 +148,7 @@ class Buddy(dbus.service.Object):
return None
return self._activities[self._current_activity]
def _set_icon(self, icon):
def set_icon(self, icon):
"""Can only set icon for other buddies. The Owner
takes care of setting it's own icon."""
if icon != self._icon:
@ -181,8 +180,8 @@ class Buddy(dbus.service.Object):
class Owner(Buddy):
"""Class representing the owner of the machine. This is the client
portion of the Owner, paired with the server portion in Owner.py."""
def __init__(self, ps, bus_name, object_id, icon_cache):
Buddy.__init__(self, bus_name, object_id, icon_cache)
def __init__(self, ps, bus_name, object_id):
Buddy.__init__(self, bus_name, object_id)
self._ps = ps
self._nick_name = profile.get_nick_name()

View File

@ -1,4 +1,5 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -14,64 +15,64 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os, time, md5
from sugar import env
from sugar import util
import os.path
import cPickle
class BuddyIconCache(object):
"""Caches icons on disk and finds them based on md5 hash."""
"""Caches icons on disk and finds them based on the jid of their owners."""
def __init__(self):
ppath = env.get_profile_path()
self._cachepath = os.path.join(ppath, "cache", "buddy-icons")
if not os.path.exists(self._cachepath):
os.makedirs(self._cachepath)
self._cachepath = os.path.join(ppath, "cache", "buddy-icons", "cache")
if not os.path.exists(self._cachepath):
self._cache = {}
else:
self._load_cache()
def _load_cache(self):
try:
self._cache = cPickle.load(open(self._cachepath, "r"))
except:
self._cache = {}
# Read all cached icons and their sums
for fname in os.listdir(self._cachepath):
m = md5.new()
data = self._get_icon_data(fname)
if len(data) == 0:
continue
m.update(data)
printable_hash = util.printable_hash(m.digest())
self._cache[printable_hash] = fname
del m
def _get_icon_data(self, fname):
fd = open(os.path.join(self._cachepath, fname), "r")
data = fd.read()
fd.close()
del fd
return data
def _save_cache(self):
out = open(self._cachepath, "w")
cPickle.dump(self._cache, out, protocol=2)
def get_icon(self, jid, token):
hit = self._cache.get(jid)
if hit:
t, icon = hit[0], hit[1]
if t == token:
return icon
def get_icon(self, printable_hash):
if not isinstance(printable_hash, unicode):
raise RuntimeError("printable_hash must be a unicode string.")
try:
fname = self._cache[printable_hash]
return self._get_icon_data(fname)
except KeyError:
pass
return None
def add_icon(self, icon_data):
if len(icon_data) == 0:
return
def store_icon(self, jid, token, data):
self._cache[jid] = (token, data)
self._save_cache()
m = md5.new()
m.update(icon_data)
printable_hash = util.printable_hash(m.digest())
if self._cache.has_key(printable_hash):
del m
return
if __name__ == "__main__":
my_cache = BuddyIconCache()
# Write the icon to disk and add an entry to our cache for it
m.update(time.asctime())
fname = util.printable_hash(m.digest())
fd = open(os.path.join(self._cachepath, fname), "w")
fd.write(icon_data)
fd.close()
self._cache[printable_hash] = fname
del m
# look for the icon in the cache
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
print icon
my_cache.store_icon("test@olpc.collabora.co.uk", "aaaa", "icon1")
# now we're sure that the icon is in the cache
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
print icon
# new icon
my_cache.store_icon("test@olpc.collabora.co.uk", "bbbb", "icon2")
# the icon in the cache is not valid now
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
print icon

View File

@ -26,8 +26,6 @@ from linklocal_plugin import LinkLocalPlugin
from buddy import Buddy, Owner
from activity import Activity
import buddyiconcache
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
_PRESENCE_INTERFACE = "org.laptop.Sugar.Presence"
@ -48,14 +46,12 @@ class PresenceService(dbus.service.Object):
self._handles = {} # tp client -> (handle -> Buddy)
self._activities = {} # activity id -> Activity
self._icon_cache = buddyiconcache.BuddyIconCache()
bus = dbus.SessionBus()
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus)
# Create the Owner object
objid = self._get_next_object_id()
self._owner = Owner(self, self._bus_name, objid, self._icon_cache)
self._owner = Owner(self, self._bus_name, objid)
self._buddies[self._owner.get_key()] = self._owner
self._registry = ManagerRegistry()
@ -68,6 +64,7 @@ class PresenceService(dbus.service.Object):
self._server_plugin.connect('status', self._server_status_cb)
self._server_plugin.connect('contact-online', self._contact_online)
self._server_plugin.connect('contact-offline', self._contact_offline)
self._server_plugin.connect('avatar-updated', self._avatar_updated)
self._server_plugin.start()
# Set up the link local connection
@ -86,9 +83,9 @@ class PresenceService(dbus.service.Object):
if not buddy:
# we don't know yet this buddy
objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, self._icon_cache, handle=handle)
buddy = Buddy(self._bus_name, objid, handle=handle)
buddy.set_key(key)
print "create buddy"
print "create buddy", key
self._buddies[key] = buddy
new_buddy = True
@ -120,6 +117,12 @@ class PresenceService(dbus.service.Object):
self._next_object_id = self._next_object_id + 1
return self._next_object_id
def _avatar_updated(self, tp, handle, avatar):
buddy = self._handles[tp].get(handle)
if buddy:
buddy.set_icon(avatar)
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def ActivityAppeared(self, activity):
pass

View File

@ -18,6 +18,7 @@
import gobject
from sugar import profile
from sugar import util
from buddyiconcache import BuddyIconCache
import logging
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
@ -38,14 +39,18 @@ class ServerPlugin(gobject.GObject):
'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_INT, gobject.TYPE_INT]))
([gobject.TYPE_INT, gobject.TYPE_INT])),
'avatar-updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
}
def __init__(self, registry):
gobject.GObject.__init__(self)
self._icon_cache = BuddyIconCache()
self._registry = registry
self._online_contacts = set()
self._online_contacts = set() # handles of online contacts
self._account = self._get_account_info()
self._ever_connected = False
@ -154,8 +159,9 @@ class ServerPlugin(gobject.GObject):
# hack
self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
self._conn[CONN_INTERFACE_AVATARS].connect_to_signal('AvatarUpdated', self._avatar_updated_cb)
#if CONN_INTERFACE_AVATARS in self._conn:
# #tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles:
@ -212,15 +218,15 @@ class ServerPlugin(gobject.GObject):
self._conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline"
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print jid, "offline"
self._online_contacts.remove(handle)
self.emit("contact-offline", handle)
def _contact_go_online(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online"
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print jid, "online"
# TODO: use the OLPC interface to get the key
key = handle
@ -241,3 +247,15 @@ class ServerPlugin(gobject.GObject):
elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle)
def _avatar_updated_cb(self, handle, new_avatar_token):
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
icon = self._icon_cache.get_icon(jid, new_avatar_token)
if not icon:
# cache miss
avatar, mime_type = self._conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
icon = ''.join(map(chr, avatar))
self._icon_cache.store_icon(jid, new_avatar_token, icon)
self.emit("avatar-updated", handle, icon)