]> git.0d.be Git - empathy.git/commitdiff
Cache avatars and RequestAvatars only when needed.
authorXavier Claessens <xclaesse@gmail.com>
Wed, 26 Sep 2007 20:16:00 +0000 (20:16 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Wed, 26 Sep 2007 20:16:00 +0000 (20:16 +0000)
2007-09-26  Xavier Claessens  <xclaesse@gmail.com>

* libempathy/empathy-utils.c:
* libempathy/empathy-utils.h:
* libempathy/empathy-avatar.c:
* libempathy/empathy-avatar.h:
* libempathy/empathy-contact-factory.c: Cache avatars and RequestAvatars
only when needed.

svn path=/trunk/; revision=320

ChangeLog
libempathy/empathy-avatar.c
libempathy/empathy-avatar.h
libempathy/empathy-contact-factory.c
libempathy/empathy-utils.c
libempathy/empathy-utils.h

index be7fd0a00a64be308282cdbb6806bd4b462b86db..5820063476353f15b58285f4132ad234f53e98c4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-09-26  Xavier Claessens  <xclaesse@gmail.com>
+
+       * libempathy/empathy-utils.c:
+       * libempathy/empathy-utils.h:
+       * libempathy/empathy-avatar.c:
+       * libempathy/empathy-avatar.h:
+       * libempathy/empathy-contact-factory.c:
+
 2007-09-26  Xavier Claessens  <xclaesse@gmail.com>
 
        * libempathy/empathy-contact-factory.c: Fix capabilities update, the NOT
index 425f23f5530c3a68ce1b25e683efc42453ac0f57..e08dd28c46d9757d85a42496fb829eefce51b61b 100644 (file)
@@ -24,6 +24,8 @@
 #include "config.h"
 
 #include "empathy-avatar.h"
+#include "empathy-utils.h"
+#include "empathy-debug.h"
 
 #define DEBUG_DOMAIN "Avatar"
 
@@ -41,26 +43,113 @@ empathy_avatar_get_type (void)
        return type_id;
 }
 
-EmpathyAvatar *
-empathy_avatar_new (guchar *data,
-                  gsize   len,
-                  gchar  *format)
+static gchar *
+avatar_get_filename (const gchar *token)
 {
-       EmpathyAvatar *avatar;
+       gchar *avatar_path;
+       gchar *avatar_file;
+       gchar *token_escaped;
 
-       g_return_val_if_fail (data != NULL, NULL);
-       g_return_val_if_fail (len > 0, NULL);
-       g_return_val_if_fail (format != NULL, NULL);
+       avatar_path = g_build_filename (g_get_home_dir (),
+                                       ".gnome2",
+                                       PACKAGE_NAME,
+                                       "avatars",
+                                       NULL);
+       g_mkdir_with_parents (avatar_path, 0700);
+
+       token_escaped = empathy_escape_as_identifier (token);
+       avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
+
+       g_free (token_escaped);
+       g_free (avatar_path);
+
+       return avatar_file;
+}
+
+static EmpathyAvatar *
+avatar_new (const guchar *data,
+           const gsize   len,
+           const gchar  *format,
+           const gchar  *token)
+{
+       EmpathyAvatar *avatar;
 
        avatar = g_slice_new0 (EmpathyAvatar);
        avatar->data = g_memdup (data, len);
        avatar->len = len;
        avatar->format = g_strdup (format);
+       avatar->token = g_strdup (token);
        avatar->refcount = 1;
 
        return avatar;
 }
 
+EmpathyAvatar *
+empathy_avatar_new (const guchar *data,
+                   const gsize   len,
+                   const gchar  *format,
+                   const gchar  *token)
+{
+       EmpathyAvatar *avatar;
+       gchar         *filename;
+       GError        *error = NULL;
+
+       g_return_val_if_fail (data != NULL, NULL);
+       g_return_val_if_fail (len > 0, NULL);
+       g_return_val_if_fail (format != NULL, NULL);
+       g_return_val_if_fail (!G_STR_EMPTY (token), NULL);
+
+       avatar = avatar_new (data, len, format, token);
+
+       /* Save to cache if not yet in it */
+       filename = avatar_get_filename (token);
+       if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+               if (!g_file_set_contents (filename, data, len, &error)) {
+                       empathy_debug (DEBUG_DOMAIN,
+                                      "Failed to save avatar in cache: %s",
+                                      error ? error->message : "No error given");
+                       g_clear_error (&error);
+               } else {
+                       empathy_debug (DEBUG_DOMAIN, "Avatar saved to %s", filename);
+               }
+       }
+       g_free (filename);
+
+       return avatar;
+}
+
+EmpathyAvatar *
+empathy_avatar_new_from_cache (const gchar *token)
+{
+       EmpathyAvatar *avatar = NULL;
+       gchar         *filename;
+       gchar         *data = NULL;
+       gsize          len;
+       GError        *error = NULL;
+
+       g_return_val_if_fail (!G_STR_EMPTY (token), NULL);
+
+       /* Load the avatar from file if it exists */
+       filename = avatar_get_filename (token);
+       if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+               if (!g_file_get_contents (filename, &data, &len, &error)) {
+                       empathy_debug (DEBUG_DOMAIN,
+                                      "Failed to load avatar from cache: %s",
+                                      error ? error->message : "No error given");
+                       g_clear_error (&error);
+               }
+       }
+
+       if (data) {
+               empathy_debug (DEBUG_DOMAIN, "Avatar loaded from %s", filename);
+               avatar = avatar_new (data, len, NULL, token);
+       }
+
+       g_free (filename);
+
+       return avatar;
+}
+
 void
 empathy_avatar_unref (EmpathyAvatar *avatar)
 {
@@ -70,6 +159,7 @@ empathy_avatar_unref (EmpathyAvatar *avatar)
        if (avatar->refcount == 0) {
                g_free (avatar->data);
                g_free (avatar->format);
+               g_free (avatar->token);
                g_slice_free (EmpathyAvatar, avatar);
        }
 }
index 6b28e8e7a6b700c881cad2764417632095d85e70..b3d69a8fefa09a0e3998a1cedd1081e78a28ba90 100644 (file)
@@ -33,15 +33,18 @@ struct _EmpathyAvatar {
        guchar *data;
        gsize   len;
        gchar  *format;
+       gchar  *token;
        guint   refcount;
 };
 
-GType          empathy_avatar_get_type (void) G_GNUC_CONST;
-EmpathyAvatar * empathy_avatar_new       (guchar       *avatar,
-                                       gsize         len,
-                                       gchar        *format);
-EmpathyAvatar * empathy_avatar_ref       (EmpathyAvatar *avatar);
-void           empathy_avatar_unref     (EmpathyAvatar *avatar);
+GType           empathy_avatar_get_type       (void) G_GNUC_CONST;
+EmpathyAvatar * empathy_avatar_new            (const guchar  *avatar,
+                                              const gsize    len,
+                                              const gchar   *format,
+                                              const gchar   *token);
+EmpathyAvatar * empathy_avatar_new_from_cache (const gchar   *token);
+EmpathyAvatar * empathy_avatar_ref            (EmpathyAvatar *avatar);
+void            empathy_avatar_unref          (EmpathyAvatar *avatar);
 
 G_END_DECLS
 
index ec0d7bac800ab0154098901b3ca624a9caaf6308..b468ccec2249967800b3c6d0a2b28dd7b0b87b6e 100644 (file)
@@ -317,6 +317,33 @@ contact_factory_aliases_changed_cb (DBusGProxy *proxy,
        }
 }
 
+static void
+contact_factory_avatar_retrieved_cb (DBusGProxy *proxy,
+                                    guint       handle,
+                                    gchar      *token,
+                                    GArray     *avatar_data,
+                                    gchar      *mime_type,
+                                    gpointer    user_data)
+{
+       ContactFactoryAccountData *account_data = user_data;
+       EmpathyContact            *contact;
+       EmpathyAvatar             *avatar;
+
+       contact = contact_factory_account_data_find_by_handle (account_data,
+                                                              handle);
+       if (!contact) {
+               return;
+       }
+
+       avatar = empathy_avatar_new (avatar_data->data,
+                                    avatar_data->len,
+                                    mime_type,
+                                    token);
+
+       empathy_contact_set_avatar (contact, avatar);
+       empathy_avatar_unref (avatar);
+}
+
 static void
 contact_factory_request_avatars_cb (DBusGProxy *proxy,
                                    GError     *error,
@@ -332,6 +359,109 @@ contact_factory_request_avatars_cb (DBusGProxy *proxy,
        contact_factory_account_data_return_call (account_data);
 }
 
+typedef struct {
+       ContactFactoryAccountData *account_data;
+       GArray                    *handles;
+} TokensData;
+
+static gboolean
+contact_factory_avatar_maybe_update (ContactFactoryAccountData *account_data,
+                                    guint                      handle,
+                                    const gchar               *token)
+{
+       EmpathyContact *contact;
+       EmpathyAvatar  *avatar;
+
+       contact = contact_factory_account_data_find_by_handle (account_data,
+                                                              handle);
+       if (!contact) {
+               return TRUE;
+       }
+
+       /* Check if the avatar changed */
+       avatar = empathy_contact_get_avatar (contact);
+       if (avatar && !empathy_strdiff (avatar->token, token)) {
+               return TRUE;
+       }
+
+       /* Check if we have an avatar */
+       if (G_STR_EMPTY (token)) {
+               empathy_contact_set_avatar (contact, NULL);
+               return TRUE;
+       }
+
+       /* The avatar changed, search the new one in the cache */
+       avatar = empathy_avatar_new_from_cache (token);
+       if (avatar) {
+               /* Got from cache, use it */
+               empathy_contact_set_avatar (contact, avatar);
+               empathy_avatar_unref (avatar);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+contact_factory_get_known_avatar_tokens_cb (DBusGProxy *proxy,
+                                           GHashTable *tokens,
+                                           GError     *error,
+                                           gpointer    user_data)
+{
+       TokensData *data = user_data;
+       gint        i;
+
+       if (error) {
+               empathy_debug (DEBUG_DOMAIN,
+                              "Error getting known avatars tokens: %s",
+                              error->message);
+               goto OUT;
+       }
+
+       /* Remove handles for which we have an avatar */
+       for (i = 0; i < data->handles->len; i++) {
+               const gchar *token;
+               guint        handle;
+
+               handle = g_array_index (data->handles, guint, i);
+               token = g_hash_table_lookup (tokens, GUINT_TO_POINTER (handle));
+
+               /* If we have no token it means CM does not know what's the
+                * avatar for that contact, we need to request it. */
+               if (!token) {
+                       continue;
+               }
+
+               /* If avatar is not updated it means we don't have it in
+                * the cache so we need to request it */
+               if (!contact_factory_avatar_maybe_update (data->account_data,
+                                                         handle, token)) {
+                       continue;
+               }
+
+               /* We don't need to request the avatar for this handle,
+                * remove it from the handles array. We need to check the handle
+                * that takes the place of the current index we are removing,
+                * so i-- is needed. */
+               g_array_remove_index_fast (data->handles, i);
+               i--;
+       }
+
+       /* Request needed avatars */
+       if (data->handles->len > 0) {
+               data->account_data->nb_pending_calls++;
+               tp_conn_iface_avatars_request_avatars_async (data->account_data->avatars_iface,
+                                                            data->handles,
+                                                            contact_factory_request_avatars_cb,
+                                                            data->account_data);
+       }
+
+OUT:
+       contact_factory_account_data_return_call (data->account_data);
+       g_array_free (data->handles, TRUE);
+       g_slice_free (TokensData, data);
+}
+
 static void
 contact_factory_avatar_updated_cb (DBusGProxy *proxy,
                                   guint       handle,
@@ -341,8 +471,13 @@ contact_factory_avatar_updated_cb (DBusGProxy *proxy,
        ContactFactoryAccountData *account_data = user_data;
        GArray                    *handles;
 
-       handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
-       g_array_insert_val (handles, 0, handle);
+       if (contact_factory_avatar_maybe_update (account_data, handle, new_token)) {
+               /* Avatar was cached, nothing to do */
+               return;
+       }
+
+       handles = g_array_new (FALSE, FALSE, sizeof (guint));
+       g_array_append_val (handles, handle);
 
        account_data->nb_pending_calls++;
        tp_conn_iface_avatars_request_avatars_async (account_data->avatars_iface,
@@ -352,31 +487,6 @@ contact_factory_avatar_updated_cb (DBusGProxy *proxy,
        g_array_free (handles, TRUE);
 }
 
-static void
-contact_factory_avatar_retrieved_cb (DBusGProxy *proxy,
-                                    guint       handle,
-                                    gchar      *token,
-                                    GArray     *avatar_data,
-                                    gchar      *mime_type,
-                                    gpointer    user_data)
-{
-       ContactFactoryAccountData *account_data = user_data;
-       EmpathyContact            *contact;
-       EmpathyAvatar             *avatar;
-
-       contact = contact_factory_account_data_find_by_handle (account_data,
-                                                              handle);
-       if (!contact) {
-               return;
-       }
-
-       avatar = empathy_avatar_new (avatar_data->data,
-                                    avatar_data->len,
-                                    mime_type);
-       empathy_contact_set_avatar (contact, avatar);
-       empathy_avatar_unref (avatar);
-}
-
 static void
 contact_factory_update_capabilities (ContactFactoryAccountData *account_data,
                                     guint                      handle,
@@ -510,11 +620,18 @@ contact_factory_request_everything (ContactFactoryAccountData *account_data,
        }
 
        if (account_data->avatars_iface) {
+               TokensData *data;
+
                account_data->nb_pending_calls++;
-               tp_conn_iface_avatars_request_avatars_async (account_data->avatars_iface,
-                                                            handles,
-                                                            contact_factory_request_avatars_cb,
-                                                            account_data);
+               data = g_slice_new (TokensData);
+               data->account_data = account_data;
+               data->handles = g_array_new (FALSE, FALSE, sizeof (guint));
+               g_array_append_vals (data->handles, handles->data, handles->len);
+
+               tp_conn_iface_avatars_get_known_avatar_tokens_async (account_data->avatars_iface,
+                                                                    handles,
+                                                                    contact_factory_get_known_avatar_tokens_cb,
+                                                                    data);
        }
 
        if (account_data->capabilities_iface) {
index a1404f4036eac53506551ac9380081421dcc9b92..06442fe0513d95c9480513cd27c781b8d82a8f94 100644 (file)
@@ -198,6 +198,82 @@ empathy_strncasecmp (const gchar *s1,
        return ret_val;
 }
 
+/* Stolen from telepathy-glib */
+gboolean
+empathy_strdiff (const gchar *left, const gchar *right)
+{
+  if ((NULL == left) != (NULL == right))
+    return TRUE;
+
+  else if (left == right)
+    return FALSE;
+
+  else
+    return (0 != strcmp (left, right));
+}
+
+/* Stolen from telepathy-glib */
+static inline gboolean
+_esc_ident_bad (gchar c, gboolean is_first)
+{
+  return ((c < 'a' || c > 'z') &&
+          (c < 'A' || c > 'Z') &&
+          (c < '0' || c > '9' || is_first));
+}
+
+/* Stolen from telepathy-glib */
+gchar *
+empathy_escape_as_identifier (const gchar *name)
+{
+  gboolean bad = FALSE;
+  size_t len = 0;
+  GString *op;
+  const gchar *ptr, *first_ok;
+
+  g_return_val_if_fail (name != NULL, NULL);
+
+  for (ptr = name; *ptr; ptr++)
+    {
+      if (_esc_ident_bad (*ptr, ptr == name))
+        {
+          bad = TRUE;
+          len += 3;
+        }
+      else
+        len++;
+    }
+
+  /* fast path if it's clean */
+  if (!bad)
+    return g_strdup (name);
+
+  /* If strictly less than ptr, first_ok is the first uncopied safe character.
+   */
+  first_ok = name;
+  op = g_string_sized_new (len);
+  for (ptr = name; *ptr; ptr++)
+    {
+      if (_esc_ident_bad (*ptr, ptr == name))
+        {
+          /* copy preceding safe characters if any */
+          if (first_ok < ptr)
+            {
+              g_string_append_len (op, first_ok, ptr - first_ok);
+            }
+          /* escape the unsafe character */
+          g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
+          /* restart after it */
+          first_ok = ptr + 1;
+        }
+    }
+  /* copy trailing safe characters if any */
+  if (first_ok < ptr)
+    {
+      g_string_append_len (op, first_ok, ptr - first_ok);
+    }
+  return g_string_free (op, FALSE);
+}
+
 gboolean
 empathy_xml_validate (xmlDoc      *doc,
                     const gchar *dtd_filename)
@@ -385,17 +461,3 @@ empathy_inspect_handle (McAccount *account,
        return name;
 }
 
-/* Stolen from telepathy-glib */
-gboolean
-empathy_strdiff (const gchar *left, const gchar *right)
-{
-  if ((NULL == left) != (NULL == right))
-    return TRUE;
-
-  else if (left == right)
-    return FALSE;
-
-  else
-    return (0 != strcmp (left, right));
-}
-
index 5669d33d10cf2af9cb7a8a7b52062b646ac29957..88d533728e3fc5c51ab68cdf1b918edeec243605 100644 (file)
@@ -66,6 +66,9 @@ gint         empathy_strcasecmp                     (const gchar     *s1,
 gint         empathy_strncasecmp                    (const gchar     *s1,
                                                    const gchar     *s2,
                                                    gsize            n);
+gboolean     empathy_strdiff                        (const gchar     *left,
+                                                    const gchar     *right);
+gchar *      empathy_escape_as_identifier           (const gchar     *name);
 
 /* XML */
 gboolean     empathy_xml_validate                   (xmlDoc          *doc,
@@ -88,8 +91,6 @@ gchar *      empathy_inspect_handle                 (McAccount       *account,
                                                     guint            handle_type);
 gchar *      empathy_inspect_channel                (McAccount       *account,
                                                     TpChan          *tp_chan);
-gboolean     empathy_strdiff                        (const gchar     *left,
-                                                    const gchar     *right);
 G_END_DECLS
 
 #endif /*  __EMPATHY_UTILS_H__ */