new icon cache system
This commit is contained in:
parent
a1f5684944
commit
4c0352e9bc
@ -32,7 +32,7 @@ class Buddy(dbus.service.Object):
|
|||||||
"""Represents another person on the network and keeps track of the
|
"""Represents another person on the network and keeps track of the
|
||||||
activities and resources they make available for sharing."""
|
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:
|
if not bus_name:
|
||||||
raise ValueError("DBus bus name must be valid")
|
raise ValueError("DBus bus name must be valid")
|
||||||
if not object_id or not isinstance(object_id, int):
|
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._activities = {} # Activity ID -> Activity
|
||||||
|
|
||||||
self._icon_cache = icon_cache
|
|
||||||
|
|
||||||
self.handles = {} # tp client -> handle
|
self.handles = {} # tp client -> handle
|
||||||
|
|
||||||
|
self._icon = None
|
||||||
self._nick_name = None
|
self._nick_name = None
|
||||||
self._color = None
|
self._color = None
|
||||||
self._key = None
|
self._key = None
|
||||||
@ -149,7 +148,7 @@ class Buddy(dbus.service.Object):
|
|||||||
return None
|
return None
|
||||||
return self._activities[self._current_activity]
|
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
|
"""Can only set icon for other buddies. The Owner
|
||||||
takes care of setting it's own icon."""
|
takes care of setting it's own icon."""
|
||||||
if icon != self._icon:
|
if icon != self._icon:
|
||||||
@ -181,8 +180,8 @@ class Buddy(dbus.service.Object):
|
|||||||
class Owner(Buddy):
|
class Owner(Buddy):
|
||||||
"""Class representing the owner of the machine. This is the client
|
"""Class representing the owner of the machine. This is the client
|
||||||
portion of the Owner, paired with the server portion in Owner.py."""
|
portion of the Owner, paired with the server portion in Owner.py."""
|
||||||
def __init__(self, ps, bus_name, object_id, icon_cache):
|
def __init__(self, ps, bus_name, object_id):
|
||||||
Buddy.__init__(self, bus_name, object_id, icon_cache)
|
Buddy.__init__(self, bus_name, object_id)
|
||||||
|
|
||||||
self._ps = ps
|
self._ps = ps
|
||||||
self._nick_name = profile.get_nick_name()
|
self._nick_name = profile.get_nick_name()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Copyright (C) 2007, Red Hat, Inc.
|
# Copyright (C) 2007, Red Hat, Inc.
|
||||||
|
# Copyright (C) 2007, Collabora Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# 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
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
import os, time, md5
|
|
||||||
from sugar import env
|
from sugar import env
|
||||||
from sugar import util
|
from sugar import util
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import cPickle
|
||||||
|
|
||||||
class BuddyIconCache(object):
|
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):
|
def __init__(self):
|
||||||
ppath = env.get_profile_path()
|
ppath = env.get_profile_path()
|
||||||
self._cachepath = os.path.join(ppath, "cache", "buddy-icons")
|
self._cachepath = os.path.join(ppath, "cache", "buddy-icons", "cache")
|
||||||
|
|
||||||
if not os.path.exists(self._cachepath):
|
if not os.path.exists(self._cachepath):
|
||||||
os.makedirs(self._cachepath)
|
self._cache = {}
|
||||||
|
else:
|
||||||
|
self._load_cache()
|
||||||
|
|
||||||
self._cache = {}
|
def _load_cache(self):
|
||||||
|
|
||||||
# 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 get_icon(self, printable_hash):
|
|
||||||
if not isinstance(printable_hash, unicode):
|
|
||||||
raise RuntimeError("printable_hash must be a unicode string.")
|
|
||||||
try:
|
try:
|
||||||
fname = self._cache[printable_hash]
|
self._cache = cPickle.load(open(self._cachepath, "r"))
|
||||||
return self._get_icon_data(fname)
|
except:
|
||||||
except KeyError:
|
self._cache = {}
|
||||||
pass
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_icon(self, icon_data):
|
def store_icon(self, jid, token, data):
|
||||||
if len(icon_data) == 0:
|
self._cache[jid] = (token, data)
|
||||||
return
|
self._save_cache()
|
||||||
|
|
||||||
m = md5.new()
|
if __name__ == "__main__":
|
||||||
m.update(icon_data)
|
my_cache = BuddyIconCache()
|
||||||
printable_hash = util.printable_hash(m.digest())
|
|
||||||
if self._cache.has_key(printable_hash):
|
# look for the icon in the cache
|
||||||
del m
|
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
|
||||||
return
|
print icon
|
||||||
|
|
||||||
# Write the icon to disk and add an entry to our cache for it
|
my_cache.store_icon("test@olpc.collabora.co.uk", "aaaa", "icon1")
|
||||||
m.update(time.asctime())
|
|
||||||
fname = util.printable_hash(m.digest())
|
# now we're sure that the icon is in the cache
|
||||||
fd = open(os.path.join(self._cachepath, fname), "w")
|
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
|
||||||
fd.write(icon_data)
|
print icon
|
||||||
fd.close()
|
|
||||||
self._cache[printable_hash] = fname
|
# new icon
|
||||||
del m
|
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
|
||||||
|
@ -26,8 +26,6 @@ from linklocal_plugin import LinkLocalPlugin
|
|||||||
|
|
||||||
from buddy import Buddy, Owner
|
from buddy import Buddy, Owner
|
||||||
from activity import Activity
|
from activity import Activity
|
||||||
import buddyiconcache
|
|
||||||
|
|
||||||
|
|
||||||
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
|
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
|
||||||
_PRESENCE_INTERFACE = "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._handles = {} # tp client -> (handle -> Buddy)
|
||||||
self._activities = {} # activity id -> Activity
|
self._activities = {} # activity id -> Activity
|
||||||
|
|
||||||
self._icon_cache = buddyiconcache.BuddyIconCache()
|
|
||||||
|
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus)
|
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus)
|
||||||
|
|
||||||
# Create the Owner object
|
# Create the Owner object
|
||||||
objid = self._get_next_object_id()
|
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._buddies[self._owner.get_key()] = self._owner
|
||||||
|
|
||||||
self._registry = ManagerRegistry()
|
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('status', self._server_status_cb)
|
||||||
self._server_plugin.connect('contact-online', self._contact_online)
|
self._server_plugin.connect('contact-online', self._contact_online)
|
||||||
self._server_plugin.connect('contact-offline', self._contact_offline)
|
self._server_plugin.connect('contact-offline', self._contact_offline)
|
||||||
|
self._server_plugin.connect('avatar-updated', self._avatar_updated)
|
||||||
self._server_plugin.start()
|
self._server_plugin.start()
|
||||||
|
|
||||||
# Set up the link local connection
|
# Set up the link local connection
|
||||||
@ -86,9 +83,9 @@ class PresenceService(dbus.service.Object):
|
|||||||
if not buddy:
|
if not buddy:
|
||||||
# we don't know yet this buddy
|
# we don't know yet this buddy
|
||||||
objid = self._get_next_object_id()
|
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)
|
buddy.set_key(key)
|
||||||
print "create buddy"
|
print "create buddy", key
|
||||||
self._buddies[key] = buddy
|
self._buddies[key] = buddy
|
||||||
new_buddy = True
|
new_buddy = True
|
||||||
|
|
||||||
@ -120,6 +117,12 @@ class PresenceService(dbus.service.Object):
|
|||||||
self._next_object_id = self._next_object_id + 1
|
self._next_object_id = self._next_object_id + 1
|
||||||
return self._next_object_id
|
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")
|
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
|
||||||
def ActivityAppeared(self, activity):
|
def ActivityAppeared(self, activity):
|
||||||
pass
|
pass
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import gobject
|
import gobject
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
from sugar import util
|
from sugar import util
|
||||||
|
from buddyiconcache import BuddyIconCache
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
|
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,
|
'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
||||||
([gobject.TYPE_PYOBJECT])),
|
([gobject.TYPE_PYOBJECT])),
|
||||||
'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
'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):
|
def __init__(self, registry):
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
|
self._icon_cache = BuddyIconCache()
|
||||||
|
|
||||||
self._registry = registry
|
self._registry = registry
|
||||||
self._online_contacts = set()
|
self._online_contacts = set() # handles of online contacts
|
||||||
self._account = self._get_account_info()
|
self._account = self._get_account_info()
|
||||||
|
|
||||||
self._ever_connected = False
|
self._ever_connected = False
|
||||||
@ -154,8 +159,9 @@ class ServerPlugin(gobject.GObject):
|
|||||||
# hack
|
# hack
|
||||||
self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
|
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:
|
#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, token in zip(subscribe_handles, tokens):
|
||||||
# for handle in subscribe_handles:
|
# for handle in subscribe_handles:
|
||||||
@ -212,15 +218,15 @@ class ServerPlugin(gobject.GObject):
|
|||||||
self._conn[CONN_INTERFACE].Disconnect()
|
self._conn[CONN_INTERFACE].Disconnect()
|
||||||
|
|
||||||
def _contact_go_offline(self, handle):
|
def _contact_go_offline(self, handle):
|
||||||
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
|
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
|
||||||
print name, "offline"
|
print jid, "offline"
|
||||||
|
|
||||||
self._online_contacts.remove(handle)
|
self._online_contacts.remove(handle)
|
||||||
self.emit("contact-offline", handle)
|
self.emit("contact-offline", handle)
|
||||||
|
|
||||||
def _contact_go_online(self, handle):
|
def _contact_go_online(self, handle):
|
||||||
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
|
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
|
||||||
print name, "online"
|
print jid, "online"
|
||||||
|
|
||||||
# TODO: use the OLPC interface to get the key
|
# TODO: use the OLPC interface to get the key
|
||||||
key = handle
|
key = handle
|
||||||
@ -241,3 +247,15 @@ class ServerPlugin(gobject.GObject):
|
|||||||
elif online and status in ["offline", "invisible"]:
|
elif online and status in ["offline", "invisible"]:
|
||||||
self._contact_go_offline(handle)
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user