[empathy] individual-manager: keep track of most popular contacts



commit c85ceea55eef6e764d62c7b1cf1e536e60b67451
Author: Guillaume Desmottes <guillaume desmottes collabora co uk>
Date:   Wed May 30 14:36:21 2012 +0200

    individual-manager: keep track of most popular contacts
    
    https://bugzilla.gnome.org/show_bug.cgi?id=677940

 libempathy/empathy-individual-manager.c |  178 +++++++++++++++++++++++++++++++
 libempathy/empathy-individual-manager.h |    3 +
 2 files changed, 181 insertions(+), 0 deletions(-)
---
diff --git a/libempathy/empathy-individual-manager.c b/libempathy/empathy-individual-manager.c
index a14f64c..e80e132 100644
--- a/libempathy/empathy-individual-manager.c
+++ b/libempathy/empathy-individual-manager.c
@@ -43,6 +43,11 @@
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualManager)
 
+/* We just expose the $TOP_INDIVIDUALS_LEN more popular individuals as that's
+ * what the view actually care about. We just want to notify it when this list
+ * changes, not when the position of every single individual is updated. */
+#define TOP_INDIVIDUALS_LEN 5
+
 /* This class only stores and refs Individuals who contain an EmpathyContact.
  *
  * This class merely forwards along signals from the aggregator and individuals
@@ -52,10 +57,22 @@ typedef struct
   FolksIndividualAggregator *aggregator;
   GHashTable *individuals; /* Individual.id -> Individual */
   gboolean contacts_loaded;
+
+  /* FolksIndividual sorted by popularity (most popular first) */
+  GSequence *individuals_pop;
+  /* The TOP_INDIVIDUALS_LEN first FolksIndividual (borrowed) from
+   * individuals_pop */
+  GList *top_individuals;
 } EmpathyIndividualManagerPriv;
 
 enum
 {
+  PROP_TOP_INDIVIDUALS = 1,
+  N_PROPS
+};
+
+enum
+{
   FAVOURITES_CHANGED,
   GROUPS_CHANGED,
   MEMBERS_CHANGED,
@@ -71,6 +88,25 @@ G_DEFINE_TYPE (EmpathyIndividualManager, empathy_individual_manager,
 static EmpathyIndividualManager *manager_singleton = NULL;
 
 static void
+individual_manager_get_property (GObject *object,
+    guint property_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyIndividualManager *self = EMPATHY_INDIVIDUAL_MANAGER (object);
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  switch (property_id)
+    {
+      case PROP_TOP_INDIVIDUALS:
+        g_value_set_pointer (value, priv->top_individuals);
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
 individual_group_changed_cb (FolksIndividual *individual,
     gchar *group,
     gboolean is_member,
@@ -91,6 +127,103 @@ individual_notify_is_favourite_cb (FolksIndividual *individual,
       is_favourite);
 }
 
+static guint
+compute_popularity (FolksIndividual *individual)
+{
+  /* TODO: we should have a better heuristic using the last time we interacted
+   * with the contact as well. */
+  return folks_interaction_details_get_im_interaction_count (
+      FOLKS_INTERACTION_DETAILS (individual));
+}
+
+static void
+check_top_individuals (EmpathyIndividualManager *self)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+  GSequenceIter *iter;
+  GList *l, *new_list = NULL;
+  gboolean modified = FALSE;
+  guint i;
+
+  iter = g_sequence_get_begin_iter (priv->individuals_pop);
+  l = priv->top_individuals;
+
+  /* Check if the TOP_INDIVIDUALS_LEN first individuals in individuals_pop are
+   * still the same as the ones in top_individuals */
+  for (i = 0; i < TOP_INDIVIDUALS_LEN && !g_sequence_iter_is_end (iter); i++)
+    {
+      FolksIndividual *individual = g_sequence_get (iter);
+      guint pop;
+
+      /* Don't include individual having 0 as pop */
+      pop = compute_popularity (individual);
+      if (pop <= 0)
+        break;
+
+      if (!modified)
+        {
+          if (l == NULL)
+            {
+              /* Old list is shorter than the new one */
+              modified = TRUE;
+            }
+          else
+            {
+              modified = (individual != l->data);
+
+              l = g_list_next (l);
+            }
+        }
+
+      new_list = g_list_prepend (new_list, individual);
+
+      iter = g_sequence_iter_next (iter);
+    }
+
+  g_list_free (priv->top_individuals);
+  priv->top_individuals = g_list_reverse (new_list);
+
+  if (modified)
+    {
+      DEBUG ("Top individuals changed:");
+
+      for (l = priv->top_individuals; l != NULL; l = g_list_next (l))
+        {
+          FolksIndividual *individual = l->data;
+
+          DEBUG ("  %s (%u)",
+              folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)),
+              compute_popularity (individual));
+        }
+
+      g_object_notify (G_OBJECT (self), "top-individuals");
+    }
+}
+
+static gint
+compare_individual_by_pop (gconstpointer a,
+    gconstpointer b,
+    gpointer user_data)
+{
+  guint pop_a, pop_b;
+
+  pop_a = compute_popularity (FOLKS_INDIVIDUAL (a));
+  pop_b = compute_popularity (FOLKS_INDIVIDUAL (b));
+
+  return pop_b - pop_a;
+}
+
+static void
+individual_notify_im_interaction_count (FolksIndividual *individual,
+    GParamSpec *pspec,
+    EmpathyIndividualManager *self)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  g_sequence_sort (priv->individuals_pop, compare_individual_by_pop, NULL);
+  check_top_individuals (self);
+}
+
 static void
 add_individual (EmpathyIndividualManager *self, FolksIndividual *individual)
 {
@@ -100,21 +233,38 @@ add_individual (EmpathyIndividualManager *self, FolksIndividual *individual)
       g_strdup (folks_individual_get_id (individual)),
       g_object_ref (individual));
 
+  g_sequence_insert_sorted (priv->individuals_pop, g_object_ref (individual),
+      compare_individual_by_pop, NULL);
+  check_top_individuals (self);
+
   g_signal_connect (individual, "group-changed",
       G_CALLBACK (individual_group_changed_cb), self);
   g_signal_connect (individual, "notify::is-favourite",
       G_CALLBACK (individual_notify_is_favourite_cb), self);
+  g_signal_connect (individual, "notify::im-interaction-count",
+      G_CALLBACK (individual_notify_im_interaction_count), self);
 }
 
 static void
 remove_individual (EmpathyIndividualManager *self, FolksIndividual *individual)
 {
   EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+  GSequenceIter *iter;
+
+  iter = g_sequence_lookup (priv->individuals_pop, individual,
+      compare_individual_by_pop, NULL);
+  if (iter != NULL)
+    {
+      g_sequence_remove (iter);
+      check_top_individuals (self);
+    }
 
   g_signal_handlers_disconnect_by_func (individual,
       individual_group_changed_cb, self);
   g_signal_handlers_disconnect_by_func (individual,
       individual_notify_is_favourite_cb, self);
+  g_signal_handlers_disconnect_by_func (individual,
+      individual_notify_im_interaction_count, self);
 
   g_hash_table_remove (priv->individuals, folks_individual_get_id (individual));
 }
@@ -257,6 +407,16 @@ individual_manager_dispose (GObject *object)
   G_OBJECT_CLASS (empathy_individual_manager_parent_class)->dispose (object);
 }
 
+static void
+individual_manager_finalize (GObject *object)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (object);
+
+  g_sequence_free (priv->individuals_pop);
+
+  G_OBJECT_CLASS (empathy_individual_manager_parent_class)->finalize (object);
+}
+
 static GObject *
 individual_manager_constructor (GType type,
     guint n_props,
@@ -302,10 +462,18 @@ static void
 empathy_individual_manager_class_init (EmpathyIndividualManagerClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *spec;
 
+  object_class->get_property = individual_manager_get_property;
   object_class->dispose = individual_manager_dispose;
+  object_class->finalize = individual_manager_finalize;
   object_class->constructor = individual_manager_constructor;
 
+  spec = g_param_spec_pointer ("top-individuals", "top individuals",
+      "Top Individuals",
+      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_TOP_INDIVIDUALS, spec);
+
   signals[GROUPS_CHANGED] =
       g_signal_new ("groups-changed",
           G_TYPE_FROM_CLASS (klass),
@@ -379,6 +547,8 @@ empathy_individual_manager_init (EmpathyIndividualManager *self)
   priv->individuals = g_hash_table_new_full (g_str_hash, g_str_equal,
       g_free, g_object_unref);
 
+  priv->individuals_pop = g_sequence_new (g_object_unref);
+
   priv->aggregator = folks_individual_aggregator_new ();
   tp_g_signal_connect_object (priv->aggregator, "individuals-changed-detailed",
       G_CALLBACK (aggregator_individuals_changed_cb), self, 0);
@@ -692,3 +862,11 @@ empathy_individual_manager_get_contacts_loaded (EmpathyIndividualManager *self)
 
   return priv->contacts_loaded;
 }
+
+GList *
+empathy_individual_manager_get_top_individuals (EmpathyIndividualManager *self)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  return priv->top_individuals;
+}
diff --git a/libempathy/empathy-individual-manager.h b/libempathy/empathy-individual-manager.h
index 3e17bbd..d2a5fe9 100644
--- a/libempathy/empathy-individual-manager.h
+++ b/libempathy/empathy-individual-manager.h
@@ -86,5 +86,8 @@ void empathy_individual_manager_set_blocked (EmpathyIndividualManager *self,
 gboolean empathy_individual_manager_get_contacts_loaded (
     EmpathyIndividualManager *self);
 
+GList * empathy_individual_manager_get_top_individuals (
+    EmpathyIndividualManager *self);
+
 G_END_DECLS
 #endif /* __EMPATHY_INDIVIDUAL_MANAGER_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]