uuencode -m share/hermes-48.png - | perl -ne 'print " $$_" unless $$. == 1 or /^====$$/' >>debian/control
py_compilefiles src/*.py
mkdir bin
- gcc -o bin/contact-update -std=c99 `pkg-config --cflags --libs libebook-1.2 glib-2.0` src/contact-update.c
install:
mkdir -p ${DESTDIR}/opt/hermes/lib ${DESTDIR}/opt/hermes/bin
ln -s ../lib/gui.py ${DESTDIR}/opt/hermes/bin/hermes
- install -D -m 0755 -o root -g root bin/contact-update ${DESTDIR}/opt/hermes/bin/contact-update
install -D -m 0644 -o root -g root src/*.py* ${DESTDIR}/opt/hermes/lib/
install -D -m 0644 -o root -g root share/hermes-64.png ${DESTDIR}/usr/share/icons/hicolor/scalable/hildon/hermes.png
install -D -m 0644 -o root -g root share/hermes-48.png ${DESTDIR}/usr/share/icons/hicolor/48x48/hildon/hermes.png
+hermes (0.0.6) unstable; urgency=low
+
+ * New icons from Tim Samoff.
+ * Set Facebook and Twitter URLs into profiles (increases data
+ enrichment, and will be used for resyncing).
+ * Manipulate contact photos directly from Python, rather than
+ using external C program.
+ * Remove "Back" buttons from contact views - not consistent with
+ Maemo 5 HIG.
+ * Implement birthday adding.
+
+ -- Andrew Flegg <andrew@bleb.org> Tue, 6 Oct 2009 22:49:22 +0100
+
hermes (0.0.5) unstable; urgency=low
* Fix gnome-python dependency (re-reported by Andrew Smith &
Section: user/utilities
Priority: extra
Maintainer: Andrew Flegg <andrew@bleb.org>
-Build-Depends: debhelper (>= 5), python-runtime | python2.5-runtime, libebook-dev,
- sharutils, libosso-abook-dev
+Build-Depends: debhelper (>= 5), python-runtime | python2.5-runtime, sharutils
Standards-Version: 3.7.2
Package: hermes
-Architecture: any
+Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, python-imaging |
python2.5-imaging, python-osso | python2.5-osso, python-hildon |
python2.5-hildon, python-twitter, python-facebook, python-evolution, gnome-python
XB-Maemo-Display-Name: Hermes
XB-Maemo-Icon-26:
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
- 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
- EevKlKQAAAAASUVORK5CYII=
+ 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
+++ /dev/null
-/**
- * contact-update (c) Andrew Flegg 2009
- * ~~~~~~~~~~~~~~ Released under the Artistic Licence.
- *
- * A very quick and dirty program to modify an EDS contact's photo and
- * birthday. This is necessary because evolution-python only exposes the two
- * structs GBoxed, which cannot be created or manipulated in Python (AFAICT).
- *
- * Error handling: limited
- * Syntax: contact-update <name> --photo <mime-type> <file>
- * contact-update <name> --birthday <year> <month> <day>
- */
-#include <libebook/e-book.h>
-#include <libebook/e-contact.h>
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <string.h>
-
-// gcc -o contact-update -std=c99 `pkg-config --cflags --libs libebook-1.2 glib-2.0` contact-update.c
-
-GError *error = NULL;
-
-
-/**
- * Simple error handling.
- *
- * @param result Result of an expression. If false, <var>message</var> will be shown
- * and the process terminated.
- * @param message Message to display if <var>result</var> is false.
- */
-void* try(gboolean result, char* message) {
- if (!result) {
- fprintf(stderr, "Error %s: %s\n", message, error ? error->message : "");
- exit(EXIT_FAILURE);
- }
-}
-
-
-/**
- * Load a photo from disk into an EContactPhoto record.
- *
- * @param mime_type MIME type to store in the photo record.
- * @param filename Filename on disk to load.
- */
-EContactPhoto load_photo(char* mime_type, char* filename) {
- struct stat stbuf;
- stat(filename, &stbuf);
- char* photo_data = malloc(stbuf.st_size);
- try(photo_data != NULL, "allocating memory for photo");
- FILE *file = fopen(filename, "r");
- try(file != NULL, "opening file");
- int read;
- char* current = photo_data;
- while ((read = fread(current, 1, stbuf.st_size, file)) > 0) {
- current += read;
- }
- fclose(file);
-
- EContactPhoto photo;
- photo.type = E_CONTACT_PHOTO_TYPE_INLINED;
- photo.data.inlined.mime_type = mime_type;
- photo.data.inlined.length = stbuf.st_size;
- photo.data.inlined.data = photo_data;
-
- return photo;
-}
-
-
-/**
- * Create a date record corresponding to the given strings.
- *
- * @param year Character representation of the year.
- * @param month Character representation of the month.
- * @param day Character representation of the day.
- */
-EContactDate read_date(char* year, char* month, char* day) {
- EContactDate date;
- date.year = atoi(year);
- date.month = atoi(month);
- date.day = atoi(day);
- return date;
-}
-
-
-/**
- * Main entry point: do the work of updating matched records.
- */
-int main(int argc, char* argv[]) {
- g_type_init();
-
- try(argc > 2, "\nsyntax: <name> --photo <mime-type> <file>\n <name> --birthday <year> <month> <day>");
-
- // -- Data structures we'll use later...
- //
- gboolean is_photo;
- gboolean is_birthday;
- EContactPhoto photo;
- EContactDate birthday;
- if (strcmp(argv[2], "--photo") == 0) {
- try(argc == 5, "syntax: <name> --photo <mime-type> <file>");
- photo = load_photo(argv[3], argv[4]);
- is_photo = 1;
-
- } else if (strcmp(argv[2], "--birthday") == 0) {
- try(argc == 6, "syntax: <name> --birthday <year> <month> <day>");
- birthday = read_date(argv[3], argv[4], argv[5]);
- is_birthday = 1;
-
- } else {
- try(1 == 0, "\nsyntax: <name> --photo <mime-type> <file>\n <name> --birthday <year> <month> <day>");
- }
-
- // -- Open the address book...
- //
- EBook *book = e_book_new_system_addressbook(&error);
- try(book != NULL, "finding address book");
- try(e_book_open(book, FALSE, &error), "opening address book");
- try(e_book_is_writable(book), "book is writable");
-
- // -- Find the list of contacts...
- //
- GList *contacts = NULL;
- EBookQuery *query = e_book_query_field_test(E_CONTACT_FULL_NAME, E_BOOK_QUERY_IS, argv[1]);
- try(e_book_get_contacts(book, query, &contacts, &error), "listing contacts");
- e_book_query_unref(query);
-
- // -- Update the contacts...
- //
- int i = 0;
- for (GList* node = g_list_first(contacts); node != NULL; node = g_list_next(node)) {
- EContact *contact = E_CONTACT(node->data);
- if (is_photo) e_contact_set(contact, E_CONTACT_PHOTO, &photo);
- if (is_birthday) e_contact_set(contact, E_CONTACT_BIRTH_DATE, &birthday);
-
- if (is_photo || is_birthday) {
- try(e_book_commit_contact(book, contact, &error), "committing contact");
- i++;
- }
- }
- printf("Modified %d records\n", i);
-
- return EXIT_SUCCESS;
-}
-
import urllib
import Image
import ImageOps
+import StringIO
+import datetime
+from pygobject import *
+from ctypes import *
+
+# Constants from http://library.gnome.org/devel/libebook/stable/EContact.html#EContactField
+ebook = CDLL('libebook-1.2.so.5')
+E_CONTACT_HOMEPAGE_URL = 42
+E_CONTACT_PHOTO = 94
+E_CONTACT_BIRTHDAY_DATE = 107
+
class ContactStore:
"""Provide an API for changing contact data. Abstracts limitations
"""Create a new contact store for modifying contacts in the given
EBook."""
- self.temp_file = os.tmpnam()
self.book = book
def close(self):
"""Close the store and tidy-up any resources."""
- if (os.path.isfile(self.temp_file)):
- os.unlink(self.temp_file)
+ pass
# -----------------------------------------------------------------------
photo is wider than it is tall, it will be cropped with a bias towards
the top of the photo."""
- urllib.urlretrieve(url, self.temp_file)
- im = Image.open(self.temp_file)
+ f = urllib.urlopen(url)
+ data = ''
+ while True:
+ read_data = f.read()
+ data += read_data
+ if not read_data:
+ break
+
+ im = Image.open(StringIO.StringIO(data))
(w, h) = im.size
if (h > w):
print "Shrinking photo for %s as it's %d x %d" % (contact.get_name(), w, h)
im = ImageOps.fit(im, (w, w), Image.NEAREST, 0, (0, 0.1))
- im.save(self.temp_file, "JPEG")
print "Updating photo for %s" % (contact.get_name())
- os.spawnl(os.P_WAIT, '/opt/hermes/bin/contact-update', 'contact-update', contact.get_name(), '--photo', 'image/jpeg', self.temp_file)
+ f = StringIO.StringIO()
+ im.save(f, "JPEG")
+ image_data = f.getvalue()
+ photo = EContactPhoto()
+ photo.type = 0
+ photo.data = EContactPhoto_data()
+ photo.data.inlined = EContactPhoto_inlined()
+ photo.data.inlined.mime_type = cast(create_string_buffer("image/jpeg"), c_char_p)
+ photo.data.inlined.length = len(image_data)
+ photo.data.inlined.data = cast(create_string_buffer(image_data), c_void_p)
+ ebook.e_contact_set(hash(contact), E_CONTACT_PHOTO, addressof(photo))
+ return True
+
+
+ # -----------------------------------------------------------------------
+ def set_birthday(self, contact, day, month, year = 0):
+ if year == 0:
+ year = datetime.date.today().year
+
+ birthday = EContactDate()
+ birthday.year = year
+ birthday.month = month
+ birthday.day = day
+ print "Setting birthday for [%s] to %d-%d-%d" % (contact.get_name(), year, month, day)
+ ebook.e_contact_set(hash(contact), E_CONTACT_BIRTHDAY_DATE, addressof(birthday))
+ return True
+
+
+ # -----------------------------------------------------------------------
+ def get_urls(self, contact):
+ """Return a list of URLs which are associated with this contact."""
+
+ urls = []
+ ai = GList.new(ebook.e_contact_get_attributes(hash(contact), E_CONTACT_HOMEPAGE_URL))
+ while ai.has_next():
+ attr = ai.next(as_a = EVCardAttribute)
+ if not attr:
+ raise Exception("Unexpected null attribute for [" + contact.get_name() + "] with URLs " + urls)
+ urls.append(string_at(attr.value().next()))
+
+ return urls
+
+
+ # -----------------------------------------------------------------------
+ def add_url(self, contact, url, unique = ''):
+ """Add a new URL to the set of URLs for the given contact."""
+
+ unique = unique or url
+ url_attr = None
+ ai = GList.new(ebook.e_contact_get_attributes(hash(contact), E_CONTACT_HOMEPAGE_URL))
+ while ai.has_next():
+ attr = ai.next(as_a = EVCardAttribute)
+ existing = string_at(attr.value().next())
+ if existing == unique or existing == url:
+ return False
+ elif existing.find(unique) > -1:
+ url_attr = attr
+ break
+
+ if not url_attr:
+ ai.add()
+ url_attr = EVCardAttribute()
+ url_attr.group = ''
+ url_attr.name = 'URL'
+
+ val = GList()
+ print "Setting URL for [%s] to [%s]" % (contact.get_name(), url)
+ val.set(create_string_buffer(url))
+ ai.set(addressof(url_attr))
+ url_attr.values = cast(addressof(val), POINTER(GList))
+ ebook.e_contact_set_attributes(hash(contact), E_CONTACT_HOMEPAGE_URL, addressof(ai))
+ return True
+
import gtk
import hildon
from ctypes import *
+from pygobject import *
class ContactView(hildon.PannableArea):
"""Widget which shows a list of contacts in a pannable area.
self.add(self.treeview)
self.set_size_request(600, 380)
-
-class EContactPhoto_inlined(Structure):
- _fields_ = [('mime_type', c_char_p),
- ('length', c_uint),
- ('data', c_void_p)]
-
-class EContactPhoto_data(Union):
- _fields_ = [('inlined', EContactPhoto_inlined),
- ('uri', c_char_p)]
-
-class EContactPhoto(Structure):
- _fields_ = [('type', c_int),
- ('data', EContactPhoto_data)]
-
-
view = contactview.ContactView(contacts)
dialog = gtk.Dialog('Contacts', self.window)
- dialog.add_button('Back', gtk.RESPONSE_OK)
#view.connect('contact-activated', self.map_contact)
dialog.vbox.add(view)
dialog.show_all()
self.do_fb_login()
# Get the list of friends...
- attrs = ['uid', 'name', 'pic_big', 'birthday_date']
+ attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url']
for friend in self.fb.users.getInfo(self.fb.friends.get(), attrs):
friend['pic'] = friend[attrs[2]]
self.friends[friend['name']] = friend
api = twitter.Api(username=user, password=passwd)
users = api.GetFriends()
for friend in api.GetFriends():
- self.friends[friend.name] = {'name': friend.name, 'pic': friend.profile_image_url, 'birthday_date': None}
+ self.friends[friend.name] = {'name': friend.name, 'pic': friend.profile_image_url, 'birthday_date': None, 'twitter_url': 'http://twitter.com/%s' % (friend.screen_name), 'homepage' : friend.url}
+ # TODO What if the user has *no* contacts?
+
# -----------------------------------------------------------------------
def sync_contacts(self, resync = False):
if name in self.friends:
friend = self.friends[name]
found = True
+ updated = False
if friend['pic'] and (resync or contact.get_property('photo') is None):
- print "Picture for %s is [%s]" % (name, friend['pic'])
- store.set_photo(contact, friend['pic'])
- self.updated.append(contact)
+ updated = store.set_photo(contact, friend['pic']) or updated
if friend['birthday_date'] and (resync or contact.get_property('birth-date') is None):
- print "Birthday for %s is [%s]" % (name, friend['birthday_date'])
+ date_str = friend['birthday_date'].split('/')
+ date_str.append('0')
+ updated = store.set_birthday(contact, int(date_str[1]),
+ int(date_str[0]),
+ int(date_str[2])) or updated
+
+ if 'profile_url' in friend and friend['profile_url']:
+ updated = store.add_url(contact, friend['profile_url'], unique='facebook.com') or updated
+
+ if 'twitter_url' in friend and friend['twitter_url']:
+ updated = store.add_url(contact, friend['twitter_url'], unique='twitter.com') or updated
+
+ if 'homepage' in friend and friend['homepage']:
+ updated = store.add_url(contact, friend['homepage']) or updated
+ if updated:
+ self.updated.append(contact)
+ addresses.commit_contact(contact)
+ print "Saved changes to [%s]" % (contact.get_name())
+
break
if found:
--- /dev/null
+from ctypes import *
+import sys
+import ctypes
+
+import gobject
+
+# -------------------------------------------------------------------------
+class _PyGObject_Functions(ctypes.Structure):
+ """GObject <-> Python mapping from http://faq.pygtk.org/index.py?req=show&file=faq23.041.htp"""
+ _fields_ = [
+ ('register_class',
+ ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.c_int, ctypes.py_object,
+ ctypes.py_object)),
+ ('register_wrapper',
+ ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
+ ('register_sinkfunc',
+ ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
+ ('lookupclass',
+ ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
+ ('newgobj',
+ ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
+ ]
+
+
+# -------------------------------------------------------------------------
+class PyGObjectCAPI(object):
+ """GObject <-> Python mapping from http://faq.pygtk.org/index.py?req=show&file=faq23.041.htp"""
+
+ def __init__(self):
+ addr = ctypes.pythonapi.PyCObject_AsVoidPtr(
+ ctypes.py_object(gobject._PyGObject_API))
+ self._api = _PyGObject_Functions.from_address(addr)
+
+ def pygobject_new(self, addr):
+ return self._api.newgobj(addr)
+
+
+# -------------------------------------------------------------------------
+class GList(Structure):
+ """GList representation and convenience functions, based on Java's Iterable.
+
+ Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
+ Released under the Artistic Licence."""
+
+
+ # -----------------------------------------------------------------------
+ @classmethod
+ def new(clazz, ptr = None):
+ """Return a reference to an empty, or valid, GList at the
+ given pointer address."""
+
+ if ptr:
+ return cast(c_void_p(ptr), POINTER(GList)).contents
+ else:
+ return GList()
+
+
+ # -----------------------------------------------------------------------
+ _fields_ = [('_data', c_void_p),
+ ('_next', c_void_p),
+ ('_prev', c_void_p)]
+
+ _ptr = None # Initialises to before the list for `while(has_next)...'
+
+
+ # -----------------------------------------------------------------------
+ def reset(self):
+ """Rewind the iterable to the start of the list."""
+
+ self._ptr = None
+
+ # -----------------------------------------------------------------------
+ def has_next(self):
+ """Return True if the list has an item on which next can be called."""
+
+ return (not self._ptr and self._data) or (self._ptr and self._ptr._next)
+
+
+ # -----------------------------------------------------------------------
+ def next(self, as_a = None):
+ """Move the pointer on to the next item in the list and return its value, or
+ raise an exception if already on the last."""
+
+ if self._ptr and not self._ptr._next:
+ raise Exception("IndexOutOfBounds")
+
+ self._ptr = self._ptr and cast(self._ptr._next, POINTER(GList)).contents or self
+ if not self._ptr._data:
+ return None
+ elif as_a:
+ return cast(self._ptr._data, POINTER(as_a)).contents
+ else:
+ return self._ptr._data
+
+
+ # -----------------------------------------------------------------------
+ def set(self, value):
+ """Set the data in the current position in the list."""
+
+ if not self._ptr:
+ self._ptr = self
+
+ self._ptr._data = cast(value, c_void_p);
+
+
+
+ # -----------------------------------------------------------------------
+ def add(self):
+ """Add a new entry on to the end of the list, ready to be "set"."""
+
+ self.reset()
+ while self.has_next():
+ self.next()
+
+ if not self._ptr:
+ self._ptr = self
+ else:
+ new = GList()
+ new._prev = addressof(self._ptr)
+ self._ptr._next = addressof(new)
+ self._ptr = new
+
+
+# -------------------------------------------------------------------------
+class EContactPhoto_inlined(Structure):
+ _fields_ = [('mime_type', c_char_p),
+ ('length', c_uint),
+ ('data', c_void_p)]
+
+class EContactPhoto_data(Union):
+ _fields_ = [('inlined', EContactPhoto_inlined),
+ ('uri', c_char_p)]
+
+class EContactPhoto(Structure):
+ _fields_ = [('type', c_int),
+ ('data', EContactPhoto_data)]
+
+class EContactDate(Structure):
+ _fields_ = [('year', c_uint),
+ ('month', c_uint),
+ ('day', c_uint)]
+
+# -------------------------------------------------------------------------
+class EVCardAttribute(Structure):
+ _fields_ = [('group', c_char_p),
+ ('name', c_char_p),
+ ('params', POINTER(GList)),
+ ('values', POINTER(GList)),]
+
+ def value(self):
+ if not self.values:
+ return None
+
+ return self.values.contents
+