[folks] Port TpfPersonaStore to high-level tp-glib APIs
- From: Xavier Claessens <xclaesse src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] Port TpfPersonaStore to high-level tp-glib APIs
- Date: Fri, 13 Apr 2012 14:40:21 +0000 (UTC)
commit ac7413f779e92d174f58da1544eb623c8ec07192
Author: Xavier Claessens <xavier claessens collabora co uk>
Date: Fri Mar 30 10:19:37 2012 +0200
Port TpfPersonaStore to high-level tp-glib APIs
It now uses Connection.ContactList iface instead of deprecated
ContactList channels.
Note that this introduce an important behaviour change: folks will
no longer pull all TpContact features, but rely on the user to define
features needed on the default AM's factory.
https://bugzilla.gnome.org/show_bug.cgi?id=630822
backends/telepathy/lib/tp-lowlevel.c | 519 +--------
backends/telepathy/lib/tp-lowlevel.h | 88 +--
backends/telepathy/lib/tpf-persona-store.vala | 1764 ++++---------------------
backends/telepathy/lib/tpf-persona.vala | 40 +-
tests/lib/telepathy/contactlist/backend.c | 12 +
5 files changed, 271 insertions(+), 2152 deletions(-)
---
diff --git a/backends/telepathy/lib/tp-lowlevel.c b/backends/telepathy/lib/tp-lowlevel.c
index c706680..c89c20e 100644
--- a/backends/telepathy/lib/tp-lowlevel.c
+++ b/backends/telepathy/lib/tp-lowlevel.c
@@ -26,112 +26,10 @@
#include <glib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
-#include <telepathy-glib/channel.h>
-#include <telepathy-glib/connection.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/util.h>
+#include <telepathy-glib/telepathy-glib.h>
#include "tp-lowlevel.h"
-GQuark
-folks_tp_lowlevel_error_quark (void)
-{
- static GQuark quark = 0;
-
- if (quark == 0)
- quark = g_quark_from_static_string ("folks-tp_lowlevel");
-
- return quark;
-}
-
-static void
-connection_ensure_channel_cb (TpConnection *conn,
- gboolean yours,
- const gchar *path,
- GHashTable *properties,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
- if (error != NULL)
- {
- g_simple_async_result_set_from_error (simple, error);
- }
- else
- {
- TpChannel *channel;
-
- /* FIXME: pass in an error here and react to it */
- channel = tp_channel_new_from_properties (conn, path, properties, NULL);
- g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (channel),
- (GDestroyNotify) g_object_unref);
-
- g_object_unref (channel);
- }
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-void
-folks_tp_lowlevel_connection_open_contact_list_channel_async (
- TpConnection *conn,
- const char *name,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
- GHashTable *request;
-
- request = tp_asv_new (TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING,
- TP_IFACE_CHANNEL_TYPE_CONTACT_LIST, TP_IFACE_CHANNEL ".TargetHandleType",
- G_TYPE_UINT, TP_HANDLE_TYPE_LIST, NULL);
-
- tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", name);
- result = g_simple_async_result_new (G_OBJECT (conn), callback, user_data,
- folks_tp_lowlevel_connection_open_contact_list_channel_finish);
- tp_cli_connection_interface_requests_call_ensure_channel (conn, -1, request,
- connection_ensure_channel_cb, result, NULL, G_OBJECT (conn));
-
- g_hash_table_unref (request);
-}
-
-/**
- * folks_tp_lowlevel_connection_open_contact_list_channel_finish:
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Finish an asynchronous operation to open a contact list channel, started with
- * folks_tp_lowlevel_connection_open_contact_list_channel_async().
- *
- * Returns: (transfer none): the ensured-valid #TpChannel
- */
-TpChannel *
-folks_tp_lowlevel_connection_open_contact_list_channel_finish (
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
- TpConnection *conn;
-
- g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE);
-
- conn = TP_CONNECTION (g_async_result_get_source_object (result));
- g_return_val_if_fail (TP_IS_CONNECTION (conn), FALSE);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
-
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (conn),
- folks_tp_lowlevel_connection_open_contact_list_channel_finish), NULL);
-
- return g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (result));
-}
-
static void
connection_get_alias_flags_cb (TpConnection *conn,
guint flags,
@@ -204,111 +102,6 @@ folks_tp_lowlevel_connection_get_alias_flags_finish (
}
static void
-get_contacts_by_handle_cb (TpConnection *conn,
- guint n_contacts,
- TpContact * const *contacts,
- guint n_failed,
- const guint *failed_handles,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
- if (error != NULL)
- {
- g_simple_async_result_set_from_error (simple, error);
- }
- else
- {
- GList *contact_list = NULL;
- guint i;
-
- for (i = 0; i < n_contacts; i++)
- contact_list = g_list_prepend (contact_list,
- g_object_ref (contacts[i]));
-
- g_simple_async_result_set_op_res_gpointer (simple, contact_list, NULL);
- }
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-/**
- * folks_tp_lowlevel_connection_get_contacts_by_handle_async:
- * @conn: the connection to use
- * @contact_handles: (array length=contact_handles_length): the contact handles
- * to get
- * @contact_handles_length: number of handles in @contact_handles
- * @features: (array length=features_length): the features to use
- * @features_length: number of features in @features
- * @callback: function to call on completion
- * @user_data: user data to pass to @callback
- *
- * Get an array of #TpContact<!-- -->s for the given contact handles.
- */
-void
-folks_tp_lowlevel_connection_get_contacts_by_handle_async (
- TpConnection *conn,
- const guint *contact_handles,
- guint contact_handles_length,
- const guint *features,
- guint features_length,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (conn), callback, user_data,
- folks_tp_lowlevel_connection_get_contacts_by_handle_finish);
-
- tp_connection_get_contacts_by_handle (conn,
- contact_handles_length,
- contact_handles,
- features_length,
- features,
- get_contacts_by_handle_cb,
- result,
- NULL,
- G_OBJECT (conn));
-}
-
-/**
- * folks_tp_lowlevel_connection_get_contacts_by_handle_finish:
- * @result: the async result
- * @error: a #GError, or %NULL
- *
- * Finish an operation started with
- * folks_tp_lowlevel_connection_get_contacts_by_handle_async().
- *
- * Return value: (element-type TelepathyGLib.Contact) (transfer full): a list of
- * #TpContact<!-- -->s
- */
-GList *
-folks_tp_lowlevel_connection_get_contacts_by_handle_finish (
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
- TpConnection *conn;
-
- g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE);
-
- conn = TP_CONNECTION (g_async_result_get_source_object (result));
- g_return_val_if_fail (TP_IS_CONNECTION (conn), FALSE);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
-
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (conn),
- folks_tp_lowlevel_connection_get_contacts_by_handle_finish), NULL);
-
- return g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (result));
-}
-
-static void
get_contacts_by_id_cb (TpConnection *conn,
guint n_contacts,
TpContact * const *contacts,
@@ -414,145 +207,6 @@ folks_tp_lowlevel_connection_get_contacts_by_id_finish (
}
static void
-connection_get_requestable_channel_classes_cb (TpProxy *conn,
- const GValue *value,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
- GPtrArray *props;
-
- if (error != NULL)
- {
- g_simple_async_result_set_from_error (simple, error);
- }
- else
- {
- props = (GPtrArray*) g_value_get_boxed (value);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_ptr_array_ref (props), (GDestroyNotify) g_ptr_array_unref);
- }
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-void
-folks_tp_lowlevel_connection_get_requestable_channel_classes_async (
- TpConnection *conn,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (conn), callback, user_data,
- folks_tp_lowlevel_connection_get_requestable_channel_classes_finish);
-
- tp_cli_dbus_properties_call_get (conn, -1,
- TP_IFACE_CONNECTION_INTERFACE_REQUESTS, "RequestableChannelClasses",
- connection_get_requestable_channel_classes_cb, result, NULL,
- G_OBJECT (conn));
-}
-
-/**
- * folks_tp_lowlevel_connection_get_requestable_channel_classes_finish:
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Retrieve the #TpConnection's RequestableChannelClasses D-Bus property.
- *
- * Returns: (transfer full): the boxed property details. Unref with
- * g_ptr_array_unref().
- */
-GPtrArray *
-folks_tp_lowlevel_connection_get_requestable_channel_classes_finish (
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
- GPtrArray *props;
- TpConnection *conn;
-
- g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
-
- conn = TP_CONNECTION (g_async_result_get_source_object (result));
- g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
-
- g_return_val_if_fail (g_simple_async_result_is_valid (result,
- G_OBJECT (conn),
- folks_tp_lowlevel_connection_get_requestable_channel_classes_finish),
- NULL);
-
- props = (GPtrArray*) g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (result));
- return g_ptr_array_ref (props);
-}
-
-static void
-group_request_channel_cb (
- TpConnection *conn,
- const gchar *object_path,
- const GError *error,
- gpointer user_data,
- GObject *list)
-{
- /* The new channel will be handled by the NewChannels handler. Here we only
- * handle the error if RequestChannel failed */
- if (error)
- {
- /* Translators: the parameter is an error message. */
- g_message (_("Error requesting a group channel: %s"), error->message);
- return;
- }
-}
-
-static void
-group_request_handles_cb (
- TpConnection *conn,
- const GArray *handles,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- guint channel_handle;
-
- if (error)
- {
- /* Translators: the parameter is an error message. */
- g_message (_("Error requesting group handles: %s"), error->message);
- return;
- }
-
- channel_handle = g_array_index (handles, guint, 0);
- tp_cli_connection_call_request_channel (conn, -1,
- TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
- TP_HANDLE_TYPE_GROUP,
- channel_handle,
- TRUE,
- group_request_channel_cb,
- NULL, NULL,
- weak_object);
-}
-
-void
-folks_tp_lowlevel_connection_create_group_async (
- TpConnection *conn,
- const char *group)
-{
- const gchar *names[] = { group, NULL };
-
- tp_cli_connection_call_request_handles (conn, -1,
- TP_HANDLE_TYPE_GROUP, names,
- group_request_handles_cb,
- NULL, NULL,
- G_OBJECT (conn));
-}
-
-static void
set_contact_alias_cb (TpConnection *conn,
const GError *error,
gpointer user_data,
@@ -581,174 +235,3 @@ folks_tp_lowlevel_connection_set_contact_alias (
g_hash_table_destroy (ht);
}
-
-static void
-iterate_on_channels (TpConnection *conn,
- const GPtrArray *channels,
- gpointer user_data,
- GObject *weak_object)
-{
- FolksTpLowlevelNewGroupChannelsCallback callback = user_data;
- GObject *cb_obj = weak_object;
- guint i;
-
- for (i = 0; i < channels->len ; i++) {
- GValueArray *arr = g_ptr_array_index (channels, i);
- const gchar *path;
- GHashTable *properties;
- TpHandleType handle_type;
- TpChannel *channel;
- GError *error = NULL;
-
- path = g_value_get_boxed (g_value_array_get_nth (arr, 0));
- properties = g_value_get_boxed (g_value_array_get_nth (arr, 1));
-
- if (tp_strdiff (tp_asv_get_string (properties,
- TP_IFACE_CHANNEL ".ChannelType"),
- TP_IFACE_CHANNEL_TYPE_CONTACT_LIST))
- continue;
-
- if (tp_asv_get_string (properties, TP_IFACE_CHANNEL ".TargetID") == NULL)
- continue;
-
- handle_type = tp_asv_get_uint32 (properties,
- TP_IFACE_CHANNEL ".TargetHandleType", NULL);
-
- if (handle_type != TP_HANDLE_TYPE_GROUP)
- continue;
-
- channel = tp_channel_new_from_properties (conn, path, properties, &error);
- if (channel == NULL) {
- /* Translators: the parameter is an error message. */
- g_message (_("Failed to create group channel: %s"), error->message);
- g_error_free (error);
- return;
- }
-
- if (callback)
- callback (channel, NULL, cb_obj);
-
- g_object_unref (channel);
- }
-}
-
-static void
-new_group_channels_cb (TpConnection *conn,
- const GPtrArray *channels,
- gpointer user_data,
- GObject *weak_object)
-{
- iterate_on_channels (conn, channels, user_data, weak_object);
-}
-
-static void
-got_channels_cb (TpProxy *conn,
- const GValue *out,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- const GPtrArray *channels;
-
- if (error != NULL) {
- /* Translators: the parameter is an error message. */
- g_message (_("Get Channels property failed: %s"), error->message);
- return;
- }
-
- channels = g_value_get_boxed (out);
- iterate_on_channels (TP_CONNECTION (conn), channels, user_data, weak_object);
-}
-
-/**
- * folks_tp_lowlevel_connection_connect_to_new_group_channels:
- * @conn: the connection to use
- * @callback: (scope call): function to call on completion
- * @user_data: (closure): user data to pass to @callback
- *
- * Connect to the NewChannels signal.
- */
-void
-folks_tp_lowlevel_connection_connect_to_new_group_channels (
- TpConnection *conn,
- FolksTpLowlevelNewGroupChannelsCallback callback,
- gpointer user_data)
-{
- /* Look for existing group channels */
- tp_cli_dbus_properties_call_get (conn, -1,
- TP_IFACE_CONNECTION_INTERFACE_REQUESTS, "Channels", got_channels_cb,
- G_CALLBACK (callback), NULL, G_OBJECT (user_data));
-
- tp_cli_connection_interface_requests_connect_to_new_channels (
- conn, new_group_channels_cb, G_CALLBACK (callback), NULL, user_data,
- NULL);
-}
-
-static void
-group_add_members_cb (TpChannel *proxy,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- if (error != NULL)
- {
- /* Translators: the first parameter is a group channel identifier and the
- * second is an error message. */
- g_message (_("Failed to add contact to group '%s': %s"),
- tp_channel_get_identifier (TP_CHANNEL (proxy)), error->message);
- return;
- }
-}
-
-static void
-group_remove_members_cb (TpChannel *proxy,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
-{
- if (error != NULL)
- {
- /* Translators: the first parameter is a group channel identifier and the
- * second is an error message. */
- g_message (_("Failed to remove contact from group '%s': %s"),
- tp_channel_get_identifier (TP_CHANNEL (proxy)), error->message);
- return;
- }
-}
-
-/* XXX: there doesn't seem to be a way to make this throw a Folks.TpLowlevelError
- * (vs. the generic GLib.Error) */
-void
-folks_tp_lowlevel_channel_group_change_membership (TpChannel *channel,
- guint handle,
- gboolean is_member,
- const gchar *message,
- GError **error)
-{
- GArray *handles;
-
- if (!TP_IS_CHANNEL (channel))
- {
- g_set_error (error, FOLKS_TP_LOWLEVEL_ERROR,
- FOLKS_TP_LOWLEVEL_ERROR_INVALID_ARGUMENT,
- /* Translators: the first parameter is a pointer address and the
- * second is a contact handle (numeric identifier). */
- _("Invalid group channel %p to add handle %d to."), channel, handle);
- }
-
- handles = g_array_new (FALSE, TRUE, sizeof (guint));
- g_array_append_val (handles, handle);
-
- if (is_member)
- {
- tp_cli_channel_interface_group_call_add_members (channel, -1, handles,
- message, group_add_members_cb, NULL, NULL, NULL);
- }
- else
- {
- tp_cli_channel_interface_group_call_remove_members (channel, -1, handles,
- message, group_remove_members_cb, NULL, NULL, NULL);
- }
-
- g_array_free (handles, TRUE);
-}
diff --git a/backends/telepathy/lib/tp-lowlevel.h b/backends/telepathy/lib/tp-lowlevel.h
index 89ed8ee..eff4875 100644
--- a/backends/telepathy/lib/tp-lowlevel.h
+++ b/backends/telepathy/lib/tp-lowlevel.h
@@ -24,58 +24,10 @@
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
-#include <telepathy-glib/channel.h>
-#include <telepathy-glib/connection.h>
-#include <telepathy-glib/contact.h>
+#include <telepathy-glib/telepathy-glib.h>
G_BEGIN_DECLS
-GQuark folks_tp_lowlevel_error_quark (void);
-#define FOLKS_TP_LOWLEVEL_ERROR (folks_tp_lowlevel_error_quark ())
-
-typedef enum {
- FOLKS_TP_LOWLEVEL_ERROR_INVALID_ARGUMENT,
-} FolksTpLowlevelError;
-
-/**
- * folks_tp_lowlevel_channel_group_change_membership:
- * @channel:
- * @handle:
- * @is_member:
- * @message: (allow-none):
- * @error:
- */
-void
-folks_tp_lowlevel_channel_group_change_membership (TpChannel *channel,
- guint handle,
- gboolean is_member,
- const gchar *message,
- GError **error);
-
-/**
- * FolksTpLowlevelNewGroupChannelsCallback:
- * @channel: (allow-none) (transfer none): the new group #TpChannel
- * @result: the #GAsyncResult to finish the async call with
- * @user_data: extra data to pass to the callback
- *
- * The callback type for
- * folks_tp_lowlevel_connection_connect_to_new_group_channels().
- */
-typedef void (*FolksTpLowlevelNewGroupChannelsCallback) (TpChannel *channel,
- GAsyncResult *result,
- gpointer user_data);
-
-void
-folks_tp_lowlevel_connection_connect_to_new_group_channels (
- TpConnection *conn,
- FolksTpLowlevelNewGroupChannelsCallback callback,
- gpointer user_data);
-
-void
-folks_tp_lowlevel_connection_create_group_async (
- TpConnection *conn,
- const char *name);
-
void
folks_tp_lowlevel_connection_set_contact_alias (
TpConnection *conn,
@@ -83,18 +35,6 @@ folks_tp_lowlevel_connection_set_contact_alias (
const gchar *alias);
void
-folks_tp_lowlevel_connection_open_contact_list_channel_async (
- TpConnection *conn,
- const char *name,
- GAsyncReadyCallback callback,
- gpointer user_data);
-
-TpChannel *
-folks_tp_lowlevel_connection_open_contact_list_channel_finish (
- GAsyncResult *result,
- GError **error);
-
-void
folks_tp_lowlevel_connection_get_alias_flags_async (
TpConnection *conn,
GAsyncReadyCallback callback,
@@ -106,21 +46,6 @@ folks_tp_lowlevel_connection_get_alias_flags_finish (
GError **error);
void
-folks_tp_lowlevel_connection_get_contacts_by_handle_async (
- TpConnection *conn,
- const guint *contact_handles,
- guint contact_handles_length,
- const guint *features,
- guint features_length,
- GAsyncReadyCallback callback,
- gpointer user_data);
-
-GList *
-folks_tp_lowlevel_connection_get_contacts_by_handle_finish (
- GAsyncResult *result,
- GError **error);
-
-void
folks_tp_lowlevel_connection_get_contacts_by_id_async (
TpConnection *conn,
const char **contact_ids,
@@ -135,17 +60,6 @@ folks_tp_lowlevel_connection_get_contacts_by_id_finish (
GAsyncResult *result,
GError **error);
-void
-folks_tp_lowlevel_connection_get_requestable_channel_classes_async (
- TpConnection *conn,
- GAsyncReadyCallback callback,
- gpointer user_data);
-
-GPtrArray *
-folks_tp_lowlevel_connection_get_requestable_channel_classes_finish (
- GAsyncResult *result,
- GError **error);
-
G_END_DECLS
#endif /* FOLKS_TP_LOWLEVEL_H */
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index 157ee9e..8ab697b 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -17,6 +17,7 @@
* Authors:
* Travis Reitter <travis reitter collabora co uk>
* Philip Withnall <philip withnall collabora co uk>
+ * Xavier Claessens <xavier claessens collabora co uk>
*/
using GLib;
@@ -29,75 +30,32 @@ extern const string BACKEND_NAME;
/**
* A persona store which is associated with a single Telepathy account. It will
- * create { link Persona}s for each of the contacts in the published, stored or
- * subscribed
- * [[http://people.collabora.co.uk/~danni/telepathy-book/chapter.channel.html|channels]]
- * of the account.
+ * create { link Persona}s for each of the contacts in the account's
+ * contact list.
*/
public class Tpf.PersonaStore : Folks.PersonaStore
{
- /* FIXME: expose the interface strings in the introspected tp-glib bindings
- */
- private static string _tp_channel_iface = "org.freedesktop.Telepathy.Channel";
- private static string _tp_channel_contact_list_type = _tp_channel_iface +
- ".Type.ContactList";
- private static string _tp_channel_channel_type = _tp_channel_iface +
- ".ChannelType";
- private static string _tp_channel_handle_type = _tp_channel_iface +
- ".TargetHandleType";
- private static string[] _undisplayed_groups =
- {
- "publish",
- "stored",
- "subscribe"
- };
- private static ContactFeature[] _contact_features =
- {
- ContactFeature.ALIAS,
- ContactFeature.AVATAR_DATA,
- ContactFeature.AVATAR_TOKEN,
- ContactFeature.CAPABILITIES,
- ContactFeature.CLIENT_TYPES,
- ContactFeature.PRESENCE,
- ContactFeature.CONTACT_INFO
- };
-
- private static GLib.Quark[] _connection_features =
- {
- TelepathyGLib.Connection.get_feature_quark_contact_info (),
- 0
- };
-
private const string[] _always_writeable_properties =
{
"is-favourite"
};
+ /* Sets of Personas exposed by this store.
+ * This is the roster + self_contact */
private HashMap<string, Persona> _personas;
private Map<string, Persona> _personas_ro;
private HashSet<Persona> _persona_set;
- /* universal, contact owner handles (not channel-specific) */
- private HashMap<uint, Persona> _handle_persona_map;
- /* Map from weakly-referenced TpContacts to their original TpHandles;
- * necessary because the handles get set to 0 before our weak_notify callback
- * is called, and we need the handle to remove the contact. */
- private HashMap<unowned Contact, uint> _weakly_referenced_contacts;
- private HashMap<Channel, HashSet<Persona>> _channel_group_personas_map;
- private HashMap<Channel, HashSet<uint>> _channel_group_incoming_adds;
- private HashMap<string, HashSet<Tpf.Persona>> _group_outgoing_adds;
- private HashMap<string, HashSet<Tpf.Persona>> _group_outgoing_removes;
- private HashMap<string, Channel> _standard_channels_unready;
- private HashMap<string, Channel> _group_channels_unready;
- private HashMap<string, Channel> _groups;
- /* FIXME: Should be HashSet<Handle> */
- private HashSet<uint> _favourite_handles;
- private Channel _publish;
- private Channel _stored;
- private Channel _subscribe;
+
+ /* Map from weakly-referenced TpContacts to their Persona.
+ * This map contains all the TpContact we know about, could be more than the
+ * the roster. Persona is kept in the map until its TpContact is disposed. */
+ private HashMap<unowned Contact, Persona> _contact_persona_map;
+
+ private HashSet<string> _favourite_ids;
private Connection _conn;
private AccountManager? _account_manager; /* only null before prepare() */
private Logger _logger;
- private Contact? _self_contact;
+ private Persona? _self_persona;
private MaybeBool _can_add_personas = MaybeBool.UNSET;
private MaybeBool _can_alias_personas = MaybeBool.UNSET;
private MaybeBool _can_group_personas = MaybeBool.UNSET;
@@ -105,8 +63,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
private bool _is_prepared = false;
private bool _prepare_pending = false;
private bool _is_quiescent = false;
- private bool _got_stored_channel_members = false;
- private bool _got_self_handle = false;
+ private bool _got_initial_members = false;
+ private bool _got_self_contact = false;
private Debug _debug;
private PersonaStoreCache _cache;
private Cancellable? _load_cache_cancellable = null;
@@ -227,8 +185,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
private void _notify_if_is_quiescent ()
{
- if (this._got_stored_channel_members == true &&
- this._got_self_handle == true &&
+ if (this._got_initial_members == true &&
+ this._got_self_contact == true &&
this._is_quiescent == false)
{
this._is_quiescent = true;
@@ -236,6 +194,13 @@ public class Tpf.PersonaStore : Folks.PersonaStore
}
}
+ private void _force_quiescent ()
+ {
+ this._got_self_contact = true;
+ this._got_initial_members = true;
+ this._notify_if_is_quiescent ();
+ }
+
/**
* The { link Persona}s exposed by this PersonaStore.
*
@@ -331,14 +296,11 @@ public class Tpf.PersonaStore : Folks.PersonaStore
debug.print_key_value_pairs (domain, level,
"ID", this.id,
"Prepared?", this._is_prepared ? "yes" : "no",
- "Has stored contact members?", this._got_stored_channel_members ? "yes" : "no",
- "Has self handle?", this._got_self_handle ? "yes" : "no",
- "Publish TpChannel", "%p".printf (this._publish),
- "Stored TpChannel", "%p".printf (this._stored),
- "Subscribe TpChannel", "%p".printf (this._subscribe),
+ "Has initial members?", this._got_initial_members ? "yes" : "no",
+ "Has self contact?", this._got_self_contact ? "yes" : "no",
"TpConnection", "%p".printf (this._conn),
"TpAccountManager", "%p".printf (this._account_manager),
- "Self-TpContact", "%p".printf (this._self_contact),
+ "Self-Persona", "%p".printf (this._self_persona),
"Can add personas?", this._format_maybe_bool (this._can_add_personas),
"Can alias personas?",
this._format_maybe_bool (this._can_alias_personas),
@@ -366,151 +328,26 @@ public class Tpf.PersonaStore : Folks.PersonaStore
debug.unindent ();
- debug.print_line (domain, level, "%u handleâPersona mappings:",
- this._handle_persona_map.size);
+ debug.print_line (domain, level, "%u contactâPersona mappings:",
+ this._contact_persona_map.size);
debug.indent ();
- var iter1 = this._handle_persona_map.map_iterator ();
+ var iter1 = this._contact_persona_map.map_iterator ();
while (iter1.next () == true)
{
debug.print_line (domain, level,
- "%u â %p", iter1.get_key (), iter1.get_value ());
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u channel group Persona sets:",
- this._channel_group_personas_map.size);
- debug.indent ();
-
- var iter2 = this._channel_group_personas_map.map_iterator ();
- while (iter2.next () == true)
- {
- debug.print_heading (domain, level,
- "Channel (%p):", iter2.get_key ());
-
- debug.indent ();
-
- foreach (var persona in iter2.get_value ())
- {
- debug.print_line (domain, level, "%p", persona);
- }
-
- debug.unindent ();
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u channel group incoming handle sets:",
- this._channel_group_incoming_adds.size);
- debug.indent ();
-
- var iter3 = this._channel_group_incoming_adds.map_iterator ();
- while (iter3.next () == true)
- {
- debug.print_heading (domain, level,
- "Channel (%p):", iter3.get_key ());
-
- debug.indent ();
-
- foreach (var handle in iter3.get_value ())
- {
- debug.print_line (domain, level, "%u", handle);
- }
-
- debug.unindent ();
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u group outgoing add sets:",
- this._group_outgoing_adds.size);
- debug.indent ();
-
- var iter4 = this._group_outgoing_adds.map_iterator ();
- while (iter4.next () == true)
- {
- debug.print_heading (domain, level, "Group (%s):", iter4.get_key ());
-
- debug.indent ();
-
- foreach (var persona in iter4.get_value ())
- {
- debug.print_line (domain, level, "%p", persona);
- }
-
- debug.unindent ();
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u group outgoing remove sets:",
- this._group_outgoing_removes.size);
- debug.indent ();
-
- var iter5 = this._group_outgoing_removes.map_iterator ();
- while (iter5.next () == true)
- {
- debug.print_heading (domain, level, "Group (%s):", iter5.get_key ());
-
- debug.indent ();
-
- foreach (var persona in iter5.get_value ())
- {
- debug.print_line (domain, level, "%p", persona);
- }
-
- debug.unindent ();
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u unready standard channels:",
- this._standard_channels_unready.size);
- debug.indent ();
-
- var iter6 = this._standard_channels_unready.map_iterator ();
- while (iter6.next () == true)
- {
- debug.print_line (domain, level,
- "%s â %p", iter6.get_key (), iter6.get_value ());
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u unready group channels:",
- this._group_channels_unready.size);
- debug.indent ();
-
- var iter7 = this._group_channels_unready.map_iterator ();
- while (iter7.next () == true)
- {
- debug.print_line (domain, level,
- "%s â %p", iter7.get_key (), iter7.get_value ());
- }
-
- debug.unindent ();
-
- debug.print_line (domain, level, "%u ready group channels:",
- this._groups.size);
- debug.indent ();
-
- var iter8 = this._groups.map_iterator ();
- while (iter8.next () == true)
- {
- debug.print_line (domain, level,
- "%s â %p", iter8.get_key (), iter8.get_value ());
+ "%s â %p", iter1.get_key ().get_identifier (), iter1.get_value ());
}
debug.unindent ();
- debug.print_line (domain, level, "%u favourite handles:",
- this._favourite_handles.size);
+ debug.print_line (domain, level, "%u favourite ids:",
+ this._favourite_ids.size);
debug.indent ();
- foreach (var handle in this._favourite_handles)
+ foreach (var id in this._favourite_ids)
{
- debug.print_line (domain, level, "%u", handle);
+ debug.print_line (domain, level, "%s", id);
}
debug.unindent ();
@@ -536,14 +373,14 @@ public class Tpf.PersonaStore : Folks.PersonaStore
if (this._conn != null)
{
- this._conn.notify["self-handle"].disconnect (
- this._self_handle_changed_cb);
+ this._conn.notify["self-contact"].disconnect (
+ this._self_contact_changed_cb);
this._conn = null;
}
- if (this._weakly_referenced_contacts != null)
+ if (this._contact_persona_map != null)
{
- var iter = this._weakly_referenced_contacts.map_iterator ();
+ var iter = this._contact_persona_map.map_iterator ();
while (iter.next () == true)
{
var contact = iter.get_key ();
@@ -551,53 +388,12 @@ public class Tpf.PersonaStore : Folks.PersonaStore
}
}
- this._weakly_referenced_contacts =
- new HashMap<unowned Contact, uint> ();
-
- this._handle_persona_map = new HashMap<uint, Persona> ();
- this._channel_group_personas_map =
- new HashMap<Channel, HashSet<Persona>> ();
- this._channel_group_incoming_adds =
- new HashMap<Channel, HashSet<uint>> ();
- this._group_outgoing_adds = new HashMap<string, HashSet<Tpf.Persona>> ();
- this._group_outgoing_removes = new HashMap<string, HashSet<Tpf.Persona>> (
- );
-
- if (this._publish != null)
- {
- this._disconnect_from_standard_channel (this._publish);
- this._publish = null;
- }
-
- if (this._stored != null)
- {
- this._disconnect_from_standard_channel (this._stored);
- this._stored = null;
- }
-
- if (this._subscribe != null)
- {
- this._disconnect_from_standard_channel (this._subscribe);
- this._subscribe = null;
- }
-
- this._standard_channels_unready = new HashMap<string, Channel> ();
- this._group_channels_unready = new HashMap<string, Channel> ();
-
- if (this._groups != null)
- {
- foreach (var channel in this._groups.values)
- {
- if (channel != null)
- this._disconnect_from_group_channel (channel);
- }
- }
+ this._contact_persona_map = new HashMap<unowned Contact, Persona> ();
this._supported_fields = new HashSet<string> ();
this._supported_fields_ro = this._supported_fields.read_only_view;
- this._groups = new HashMap<string, Channel> ();
- this._favourite_handles = new HashSet<uint> ();
- this._self_contact = null;
+ this._favourite_ids = new HashSet<string> ();
+ this._self_persona = null;
}
private void _remove_store ()
@@ -661,6 +457,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
this._logger_invalidated_cb);
this._logger.favourite_contacts_changed.connect (
this._favourite_contacts_changed_cb);
+ this._initialise_favourite_contacts.begin ();
}
catch (GLib.Error e)
{
@@ -683,11 +480,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
/* If we're disconnected, advertise personas from the cache
* instead. */
yield this._load_cache ();
-
- /* We've reached a quiescent state. */
- this._got_self_handle = true;
- this._got_stored_channel_members = true;
- this._notify_if_is_quiescent ();
+ this._force_quiescent ();
}
this._is_prepared = true;
@@ -732,33 +525,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
try
{
var contacts = yield this._logger.get_favourite_contacts ();
-
- if (contacts.length == 0)
- return;
-
- /* Note that we don't need to release these handles, as they're
- * also held by the relevant contact objects, and will be released
- * as appropriate by those objects (we're circumventing tp-glib's
- * handle reference counting). */
- this._conn.request_handles (-1, HandleType.CONTACT, contacts,
- (c, ht, h, i, e, w) =>
- {
- try
- {
- this._change_favourites_by_request_handles ((Handle[]) h, i,
- e, true);
- }
- catch (GLib.Error e)
- {
- /* Translators: the parameter is an error message. */
- warning (_("Couldn't get list of favorite contacts: %s"),
- e.message);
- }
- },
- this);
- /* FIXME: Have to pass this as weak_object parameter since Vala
- * seems to swap the order of user_data and weak_object in the
- * callback. */
+ this._favourite_contacts_changed_cb (contacts, {});
}
catch (GLib.Error e)
{
@@ -767,96 +534,34 @@ public class Tpf.PersonaStore : Folks.PersonaStore
}
}
- private void _change_favourites_by_request_handles (Handle[] handles,
- string[] ids, GLib.Error? error, bool add) throws GLib.Error
+ private Persona? _lookup_persona_by_id (string id)
{
- if (error != null)
- throw error;
-
- for (var i = 0; i < handles.length; i++)
+ /* This is not efficient, but probably better than doing DBus roundtrip
+ * to get a TpContact. Maybe we should add a id->persona map? */
+ var iter = this._contact_persona_map.map_iterator ();
+ while (iter.next ())
{
- var h = handles[i];
- var p = this._handle_persona_map[h];
-
- /* Add/Remove the handle to the set of favourite handles, since we
- * might not have the corresponding contact yet */
- if (add)
- this._favourite_handles.add (h);
- else
- this._favourite_handles.remove (h);
-
- /* If the persona isn't in the _handle_persona_map yet, it's most
- * likely because the account hasn't connected yet (and we haven't
- * received the roster). If there are already entries in
- * _handle_persona_map, the account *is* connected and we should
- * warn about the unknown persona.
- * We have to take into account that this._self_contact may be
- * retrieved before or after the rest of the account's contact list,
- * affecting the size of this._handle_persona_map. */
- if (p == null &&
- ((this._self_contact == null &&
- this._handle_persona_map.size > 0) ||
- (this._self_contact != null &&
- this._handle_persona_map.size > 1)))
- {
- /* Translators: the parameter is an identifier. */
- warning (_("Unknown Telepathy contact â%sâ in favorites list."),
- ids[i]);
- continue;
- }
-
- /* Mark or unmark the persona as a favourite */
- if (p != null)
- p.is_favourite = add;
+ if (iter.get_key().get_identifier() == id)
+ return iter.get_value();
}
+ return null;
}
private void _favourite_contacts_changed_cb (string[] added, string[] removed)
{
- /* Don't listen to favourites updates if the account is disconnected. */
- if (this._conn == null)
- return;
-
- /* Add favourites */
- if (added.length > 0)
+ foreach (var id in added)
{
- this._conn.request_handles (-1, HandleType.CONTACT, added,
- (c, ht, h, i, e, w) =>
- {
- try
- {
- this._change_favourites_by_request_handles ((Handle[]) h,
- i, e, true);
- }
- catch (GLib.Error e)
- {
- /* Translators: the parameter is an error message. */
- warning (_("Couldn't add favorite contacts: %s"),
- e.message);
- }
- },
- this);
+ this._favourite_ids.add (id);
+ Persona ?p = this._lookup_persona_by_id (id);
+ if (p != null)
+ p.is_favourite = true;
}
-
- /* Remove favourites */
- if (removed.length > 0)
+ foreach (var id in removed)
{
- this._conn.request_handles (-1, HandleType.CONTACT, removed,
- (c, ht, h, i, e, w) =>
- {
- try
- {
- this._change_favourites_by_request_handles ((Handle[]) h,
- i, e, false);
- }
- catch (GLib.Error e)
- {
- /* Translators: the parameter is an error message. */
- warning (_("Couldn't remove favorite contacts: %s"),
- e.message);
- }
- },
- this);
+ this._favourite_ids.remove (id);
+ Persona ?p = this._lookup_persona_by_id (id);
+ if (p != null)
+ p.is_favourite = false;
}
}
@@ -914,9 +619,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
/* If the persona store starts offline, we've reached a quiescent
* state. */
- this._got_self_handle = true;
- this._got_stored_channel_members = true;
- this._notify_if_is_quiescent ();
+ this._force_quiescent ();
return;
}
@@ -930,130 +633,85 @@ public class Tpf.PersonaStore : Folks.PersonaStore
this, this.id);
/* Ensure the connection is prepared as necessary. */
- yield this.account.connection.prepare_async (this._connection_features);
-
- // We're connected, so can stop advertising personas from the cache
- this._unload_cache ();
+ yield this.account.connection.prepare_async ({
+ TelepathyGLib.Connection.get_feature_quark_contact_list (),
+ TelepathyGLib.Connection.get_feature_quark_contact_groups (),
+ TelepathyGLib.Connection.get_feature_quark_contact_info (),
+ TelepathyGLib.Connection.get_feature_quark_connected ()
+ });
- var conn = this.account.connection;
- conn.notify["connection-ready"].connect (this._connection_ready_cb);
+ if (!this.account.connection.has_interface_by_id (
+ iface_quark_connection_interface_contact_list ()))
+ {
+ warning ("Connection does not implement ContactList iface,
+ legacy CM are not supported anymore. Stay offline");
- /* Deal with the case where the connection is already ready
- * FIXME: We have to access the property manually until bgo#571348 is
- * fixed. */
- var connection_ready = false;
- conn.get ("connection-ready", out connection_ready);
+ this._force_quiescent ();
- if (connection_ready == true)
- this._connection_ready_cb (conn, null);
- else
- yield conn.prepare_async (null);
- }
+ return;
+ }
- private void _connection_ready_cb (Object s, ParamSpec? p)
- {
- debug ("_connection_ready_cb() for Tpf.PersonaStore %p ('%s').",
- this, this.id);
+ // We're connected, so can stop advertising personas from the cache
+ this._unload_cache ();
- var c = (Connection) s;
- FolksTpLowlevel.connection_connect_to_new_group_channels (c,
- this._new_group_channels_cb);
+ this._conn = this.account.connection;
this._marshall_supported_fields ();
this.notify_property ("supported-fields");
- FolksTpLowlevel.connection_get_alias_flags_async.begin (c, (s2, res) =>
- {
- var new_can_alias = MaybeBool.FALSE;
- try
- {
- var flags =
- FolksTpLowlevel.connection_get_alias_flags_async.end (res);
- if ((flags &
- ConnectionAliasFlags.CONNECTION_ALIAS_FLAG_USER_SET) > 0)
- {
- new_can_alias = MaybeBool.TRUE;
- }
- }
- catch (GLib.Error e)
- {
- GLib.warning (
- /* Translators: the first parameter is the display name for
- * the Telepathy account, and the second is an error
- * message. */
- _("Failed to determine whether we can set aliases on Telepathy account '%s': %s"),
- this.display_name, e.message);
- }
-
- this._can_alias_personas = new_can_alias;
- this.notify_property ("can-alias-personas");
- });
-
- FolksTpLowlevel.connection_get_requestable_channel_classes_async.begin (c,
- (s3, res3) =>
- {
- var new_can_group = MaybeBool.FALSE;
- try
- {
- GenericArray<weak void*> v;
- int i;
-
- v = FolksTpLowlevel.
- connection_get_requestable_channel_classes_async.end (res3);
-
- for (i = 0; i < v.length; i++)
- {
- unowned ValueArray @class = (ValueArray) v.get (i);
- var val = @class.get_nth (0);
- if (val != null)
- {
- var props = (HashTable<weak string, weak Value?>)
- val.get_boxed ();
-
- var channel_type = TelepathyGLib.asv_get_string (props,
- this._tp_channel_channel_type);
- bool handle_type_valid;
- var handle_type = TelepathyGLib.asv_get_uint32 (props,
- this._tp_channel_handle_type,
- out handle_type_valid);
-
- if ((channel_type ==
- this._tp_channel_contact_list_type) &&
- handle_type_valid &&
- (handle_type == HandleType.GROUP))
- {
- new_can_group = MaybeBool.TRUE;
- break;
- }
- }
- }
- }
- catch (GLib.Error e3)
- {
- GLib.warning (
- /* Translators: the first parameter is the display name for
- * the Telepathy account, and the second is an error
- * message. */
- _("Failed to determine whether we can set groups on Telepathy account '%s': %s"),
- this.display_name, e3.message);
- }
-
- this._can_group_personas = new_can_group;
- this.notify_property ("can-group-personas");
- });
-
- this._add_standard_channel (c, "publish");
- this._add_standard_channel (c, "stored");
- this._add_standard_channel (c, "subscribe");
- this._conn = c;
+ if (this._conn.get_group_storage () != ContactMetadataStorageType.NONE)
+ this._can_group_personas = MaybeBool.TRUE;
+ else
+ this._can_group_personas = MaybeBool.FALSE;
+ this.notify_property ("can-group-personas");
+
+ if (this._conn.get_can_change_contact_list ())
+ {
+ this._can_add_personas = MaybeBool.TRUE;
+ this._can_remove_personas = MaybeBool.TRUE;
+ }
+ else
+ {
+ this._can_add_personas = MaybeBool.FALSE;
+ this._can_remove_personas = MaybeBool.FALSE;
+ }
+ this.notify_property ("can-add-personas");
+ this.notify_property ("can-remove-personas");
/* Add the local user */
- _conn.notify["self-handle"].connect (this._self_handle_changed_cb);
- if (this._conn.self_handle != 0)
- this._self_handle_changed_cb (this._conn, null);
+ this._conn.notify["self-contact"].connect (this._self_contact_changed_cb);
+ this._self_contact_changed_cb (this._conn, null);
+
+ /* TpConnection still does not have high-level API for this */
+ FolksTpLowlevel.connection_get_alias_flags_async.begin (this._conn, (s2, res) =>
+ {
+ var new_can_alias = MaybeBool.FALSE;
+ try
+ {
+ var flags =
+ FolksTpLowlevel.connection_get_alias_flags_async.end (res);
+ if ((flags &
+ ConnectionAliasFlags.CONNECTION_ALIAS_FLAG_USER_SET) > 0)
+ {
+ new_can_alias = MaybeBool.TRUE;
+ }
+ }
+ catch (GLib.Error e)
+ {
+ GLib.warning (
+ /* Translators: the first parameter is the display name for
+ * the Telepathy account, and the second is an error
+ * message. */
+ _("Failed to determine whether we can set aliases on Telepathy account '%s': %s"),
+ this.display_name, e.message);
+ }
+
+ this._can_alias_personas = new_can_alias;
+ this.notify_property ("can-alias-personas");
+ });
- /* We can only initialise the favourite contacts once _conn is prepared */
- this._initialise_favourite_contacts.begin ();
+ this._conn.notify["contact-list-state"].connect (this._contact_list_state_changed_cb);
+ this._contact_list_state_changed_cb (this._conn, null);
}
private void _marshall_supported_fields ()
@@ -1180,470 +838,164 @@ public class Tpf.PersonaStore : Folks.PersonaStore
this._cached = false;
}
- private void _self_handle_changed_cb (Object s, ParamSpec? p)
+ private bool _add_persona (Persona p)
{
- var c = (Connection) s;
-
- /* Remove the old self persona */
- if (this._self_contact != null)
- this._ignore_by_handle (this._self_contact.handle, null, null, 0);
-
- if (c.self_handle == 0)
+ if (!this._persona_set.contains (p))
{
- /* We can only claim to have reached a quiescent state once we've
- * got the stored contact list and the self handle. */
- this._got_self_handle = true;
- this._notify_if_is_quiescent ();
-
- return;
+ this._personas.set (p.iid, p);
+ this._persona_set.add (p);
+ return true;
}
- uint[] contact_handles = { c.self_handle };
-
- /* We have to do it this way instead of using
- * TpLowleve.get_contacts_by_handle_async() as we're in a notification
- * callback */
- c.get_contacts_by_handle (contact_handles,
- (uint[]) this._contact_features,
- (conn, contacts, failed, error, weak_object) =>
- {
- if (error != null)
- {
- warning (
- /* Translators: the first parameter is a Telepathy handle,
- * and the second is an error message. */
- _("Failed to create contact for self handle '%u': %s"),
- conn.self_handle, error.message);
- return;
- }
-
- debug ("Creating persona from self-handle");
-
- /* Add the local user */
- Contact contact = contacts[0];
- bool added;
- Persona persona = this._add_persona_from_contact (contact, false, out added);
-
- var personas = new HashSet<Persona> ();
- if (added)
- personas.add (persona);
-
- this._self_contact = contact;
- this._emit_personas_changed (personas, null);
-
- this._got_self_handle = true;
- this._notify_if_is_quiescent ();
- },
- this);
+ return false;
}
- private void _new_group_channels_cb (TelepathyGLib.Channel? channel,
- GLib.AsyncResult? result)
+ private bool _remove_persona (Persona p)
{
- if (channel == null)
+ if (this._persona_set.contains (p))
{
- /* Translators: do not translate "NewChannels", as it's a D-Bus
- * signal name. */
- warning (_("Error creating channel for NewChannels signal."));
- return;
+ this._personas.unset (p.iid);
+ this._persona_set.remove (p);
+ return true;
}
- this._set_up_new_group_channel (channel);
- this._channel_group_changes_resolve (channel);
+ return false;
}
- private void _channel_group_changes_resolve (Channel channel)
+ private void _contact_weak_notify_cb (Object obj)
{
- unowned string group = channel.get_identifier ();
-
- var change_maps = new HashMap<HashSet<Tpf.Persona>, bool> ();
- if (this._group_outgoing_adds[group] != null)
- change_maps.set (this._group_outgoing_adds[group], true);
-
- if (this._group_outgoing_removes[group] != null)
- change_maps.set (this._group_outgoing_removes[group], false);
+ if (this._contact_persona_map == null)
+ return;
- if (change_maps.size < 1)
+ var contact = obj as Contact;
+ var persona = this._contact_persona_map[contact];
+ if (persona == null)
return;
- foreach (var entry in change_maps.entries)
+ if (this._remove_persona (persona))
{
- var changes = entry.key;
-
- foreach (var persona in changes)
- {
- try
- {
- FolksTpLowlevel.channel_group_change_membership (channel,
- (Handle) persona.contact.handle, entry.value, null);
- }
- catch (GLib.Error e)
- {
- if (entry.value == true)
- {
- /* Translators: the parameter is a persona identifier and
- * the second parameter is a group name. */
- warning (_("Failed to add Telepathy contact â%sâ to group â%sâ."),
- persona.contact != null ?
- persona.contact.identifier :
- "(nil)",
- group);
- }
- else
- {
- warning (
- /* Translators: the parameter is a persona identifier
- * and the second parameter is a group name. */
- _("Failed to remove Telepathy contact â%sâ from group â%sâ."),
-
- persona.contact != null ?
- persona.contact.identifier :
- "(nil)",
- group);
- }
- }
- }
-
- changes.clear ();
+ /* This should never happen because TpConnection keeps a ref on
+ * self and roster TpContacts, so they should have been removed
+ * already. But deal with it just in case... */
+ warning ("A TpContact part of the ContactList is disposed");
+ var personas = new HashSet<Persona> ();
+ personas.add (persona);
+ this._emit_personas_changed (null, personas);
}
+
+ this._contact_persona_map.unset (contact);
}
- private void _set_up_new_standard_channel (Channel channel)
+ internal Tpf.Persona _ensure_persona_for_contact (Contact contact)
{
- debug ("Setting up new standard channel '%s' for Tpf.PersonaStore " +
- "%p ('%s').", this, this.id, channel.get_identifier ());
-
- /* hold a ref to the channel here until it's ready, so it doesn't
- * disappear */
- this._standard_channels_unready[channel.get_identifier ()] = channel;
-
- channel.notify["channel-ready"].connect ((s, p) =>
- {
- var c = (Channel) s;
- unowned string name = c.get_identifier ();
-
- debug ("Channel '%s' is ready.", name);
-
- if (name == "publish")
- {
- this._publish = c;
-
- c.group_members_changed_detailed.connect (
- this._publish_channel_group_members_changed_detailed_cb);
- }
- else if (name == "stored")
- {
- this._stored = c;
-
- c.group_members_changed_detailed.connect (
- this._stored_channel_group_members_changed_detailed_cb);
- }
- else if (name == "subscribe")
- {
- this._subscribe = c;
-
- c.group_members_changed_detailed.connect (
- this._subscribe_channel_group_members_changed_detailed_cb);
-
- c.group_flags_changed.connect (
- this._subscribe_channel_group_flags_changed_cb);
-
- this._subscribe_channel_group_flags_changed_cb (c,
- c.group_get_flags (), 0);
- }
-
- this._standard_channels_unready.unset (name);
+ Persona? persona = this._contact_persona_map[contact];
+ if (persona != null)
+ return (!) persona;
- c.invalidated.connect (this._channel_invalidated_cb);
+ persona = new Tpf.Persona (contact, this);
+ this._contact_persona_map[contact] = persona;
+ contact.weak_ref (this._contact_weak_notify_cb);
- unowned Intset? members = c.group_get_members ();
- if (members != null && name == "stored")
- {
- this._channel_group_pend_incoming_adds.begin (c,
- members.to_array (), true, (obj, res) =>
- {
- this._channel_group_pend_incoming_adds.end (res);
+ persona.is_favourite = this._favourite_ids.contains (contact.get_identifier ());
- /* We've got some members for the stored channel group. */
- this._got_stored_channel_members = true;
- this._notify_if_is_quiescent ();
- });
- }
- else if (members != null)
- {
- this._channel_group_pend_incoming_adds.begin (c,
- members.to_array (), true);
- }
- });
+ return persona;
}
- private void _disconnect_from_standard_channel (Channel channel)
+ private void _self_contact_changed_cb (Object s, ParamSpec? p)
{
- var name = channel.get_identifier ();
- debug ("Disconnecting from channel '%s' for Tpf.PersonaStore %p ('%s').",
- name, this, this.id);
-
- channel.invalidated.disconnect (this._channel_invalidated_cb);
-
- if (name == "publish")
- {
- channel.group_members_changed_detailed.disconnect (
- this._publish_channel_group_members_changed_detailed_cb);
- }
- else if (name == "stored")
- {
- channel.group_members_changed_detailed.disconnect (
- this._stored_channel_group_members_changed_detailed_cb);
- }
- else if (name == "subscribe")
- {
- channel.group_members_changed_detailed.disconnect (
- this._subscribe_channel_group_members_changed_detailed_cb);
- channel.group_flags_changed.disconnect (
- this._subscribe_channel_group_flags_changed_cb);
- }
- }
+ var contact = this._conn.self_contact;
- private void _publish_channel_group_members_changed_detailed_cb (
- Channel channel,
- /* FIXME: Array<uint> => Array<Handle>; parser bug */
- Array<uint> added,
- Array<uint> removed,
- Array<uint> local_pending,
- Array<uint> remote_pending,
- HashTable details)
- {
- if (added.length > 0)
- this._channel_group_pend_incoming_adds.begin (channel, added, true);
+ var personas_added = new HashSet<Persona> ();
+ var personas_removed = new HashSet<Persona> ();
- /* we refuse to send these contacts our presence, so remove them */
- for (var i = 0; i < removed.length; i++)
+ /* Remove old self persona if not also part of roster */
+ if (this._self_persona != null &&
+ !this._self_persona.is_in_contact_list &&
+ this._remove_persona (this._self_persona))
{
- var handle = removed.index (i);
- this._ignore_by_handle_if_needed (handle, details);
+ personas_removed.add (this._self_persona);
}
+ this._self_persona = null;
- /* FIXME: continue for the other arrays */
- }
-
- private void _stored_channel_group_members_changed_detailed_cb (
- Channel channel,
- /* FIXME: Array<uint> => Array<Handle>; parser bug */
- Array<uint> added,
- Array<uint> removed,
- Array<uint> local_pending,
- Array<uint> remote_pending,
- HashTable details)
- {
- if (added.length > 0)
+ if (contact != null)
{
- this._channel_group_pend_incoming_adds.begin (channel, added, true,
- (obj, res) =>
- {
- this._channel_group_pend_incoming_adds.end (res);
+ debug ("Creating persona from self-contact");
- /* We can only claim to have reached a quiescent state once we've
- * got the stored contact list and the self handle. */
- this._got_stored_channel_members = true;
- this._notify_if_is_quiescent ();
- });
+ /* Add the local user to roster */
+ this._self_persona = this._ensure_persona_for_contact (contact);
+ if (this._add_persona (this._self_persona))
+ personas_added.add (this._self_persona);
}
- for (var i = 0; i < removed.length; i++)
- {
- var handle = removed.index (i);
- this._ignore_by_handle_if_needed (handle, details);
- }
+ this._emit_personas_changed (personas_added, personas_removed);
+
+ this._got_self_contact = true;
+ this._notify_if_is_quiescent ();
}
- private void _subscribe_channel_group_flags_changed_cb (
- Channel? channel,
- uint added,
- uint removed)
+ private void _contact_list_state_changed_cb (Object s, ParamSpec? p)
{
- this._update_capability ((ChannelGroupFlags) added,
- (ChannelGroupFlags) removed, ChannelGroupFlags.CAN_ADD,
- ref this._can_add_personas, "can-add-personas");
+ if (this._conn.contact_list_state != ContactListState.SUCCESS)
+ return;
- this._update_capability ((ChannelGroupFlags) added,
- (ChannelGroupFlags) removed, ChannelGroupFlags.CAN_REMOVE,
- ref this._can_remove_personas, "can-remove-personas");
+ this._conn.contact_list_changed.connect (this._contact_list_changed_cb);
+ this._contact_list_changed_cb (this._conn.dup_contact_list (),
+ new GLib.GenericArray<TelepathyGLib.Contact> ());
+
+ this._got_initial_members = true;
+ this._notify_if_is_quiescent ();
}
- private void _update_capability (
- ChannelGroupFlags added,
- ChannelGroupFlags removed,
- ChannelGroupFlags tp_flag,
- ref MaybeBool private_member,
- string prop_name)
+ private void _contact_list_changed_cb (GLib.GenericArray<TelepathyGLib.Contact> added,
+ GLib.GenericArray<TelepathyGLib.Contact> removed)
{
- var new_value = private_member;
+ var personas_added = new HashSet<Persona> ();
+ var personas_removed = new HashSet<Persona> ();
- if ((added & tp_flag) != 0)
- new_value = MaybeBool.TRUE;
+ foreach (Contact contact in added.data)
+ {
+ var persona = this._ensure_persona_for_contact (contact);
- if ((removed & tp_flag) != 0)
- new_value = MaybeBool.FALSE;
+ if (!persona.is_in_contact_list)
+ persona.is_in_contact_list = true;
- if (new_value != private_member)
- {
- private_member = new_value;
- this.notify_property (prop_name);
+ if (this._add_persona (persona))
+ personas_added.add (persona);
}
- }
- private void _subscribe_channel_group_members_changed_detailed_cb (
- Channel channel,
- /* FIXME: Array<uint> => Array<Handle>; parser bug */
- Array<uint> added,
- Array<uint> removed,
- Array<uint> local_pending,
- Array<uint> remote_pending,
- HashTable details)
- {
- if (added.length > 0)
+ foreach (Contact contact in removed.data)
{
- this._channel_group_pend_incoming_adds.begin (channel, added, true);
+ var persona = this._contact_persona_map[contact];
- /* expose ourselves to anyone we can see */
- if (this._publish != null)
+ if (persona == null)
{
- this._channel_group_pend_incoming_adds.begin (this._publish,
- added, true);
+ warning ("Unknown TpContact removed from ContactList: %s",
+ contact.get_identifier ());
+ continue;
}
- }
- /* these contacts refused to send us their presence, so remove them */
- for (var i = 0; i < removed.length; i++)
- {
- var handle = removed.index (i);
- this._ignore_by_handle_if_needed (handle, details);
+ if (persona == this._self_persona)
+ {
+ persona.is_in_contact_list = false;
+ continue;
+ }
+
+ if (this._remove_persona (persona))
+ personas_removed.add (persona);
}
- /* FIXME: continue for the other arrays */
+ this._emit_personas_changed (personas_added, personas_removed);
}
- private void _channel_invalidated_cb (TelepathyGLib.Proxy proxy, uint domain,
- int code, string message)
+ internal async void _change_group_membership (Folks.Persona persona,
+ string group, bool is_member)
{
- var channel = (Channel) proxy;
-
- this._channel_group_personas_map.unset (channel);
- this._channel_group_incoming_adds.unset (channel);
+ var tp_persona = (Tpf.Persona) persona;
- if (proxy == this._publish)
- this._publish = null;
- else if (proxy == this._stored)
- this._stored = null;
- else if (proxy == this._subscribe)
- this._subscribe = null;
+ if (is_member)
+ tp_persona.contact.add_to_group_async (group);
else
- {
- var error = new GLib.Error ((Quark) domain, code, "%s", message);
- var name = channel.get_identifier ();
- this.group_removed (name, error);
- this._groups.unset (name);
- }
- }
-
- private void _ignore_by_handle_if_needed (uint handle,
- HashTable<string, HashTable<string, Value?>> details)
- {
- unowned TelepathyGLib.Intset members;
-
- if (this._subscribe != null)
- {
- members = this._subscribe.group_get_members ();
- if (members.is_member (handle))
- return;
-
- members = this._subscribe.group_get_remote_pending ();
- if (members.is_member (handle))
- return;
- }
-
- if (this._publish != null)
- {
- members = this._publish.group_get_members ();
- if (members.is_member (handle))
- return;
- }
-
- unowned string message = TelepathyGLib.asv_get_string (details,
- "message");
- bool valid;
- Persona? actor = null;
- var actor_handle = TelepathyGLib.asv_get_uint32 (details, "actor",
- out valid);
- if (actor_handle > 0 && valid)
- actor = this._handle_persona_map[actor_handle];
-
- GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE;
- var tp_reason = TelepathyGLib.asv_get_uint32 (details, "change-reason",
- out valid);
- if (valid)
- reason = Tpf.PersonaStore._change_reason_from_tp_reason (tp_reason);
-
- this._ignore_by_handle (handle, message, actor, reason);
- }
-
- private static GroupDetails.ChangeReason _change_reason_from_tp_reason (
- uint reason)
- {
- return (GroupDetails.ChangeReason) reason;
- }
-
- private void _ignore_by_handle (uint handle, string? message, Persona? actor,
- GroupDetails.ChangeReason reason)
- {
- var persona = this._handle_persona_map[handle];
-
- debug ("Ignoring handle %u (persona: %p)", handle, persona);
-
- if (this._self_contact != null && this._self_contact.handle == handle)
- this._self_contact = null;
-
- /*
- * remove all handle-keyed entries
- */
- this._handle_persona_map.unset (handle);
-
- /* skip _channel_group_incoming_adds because they occurred after removal
- */
-
- if (persona == null)
- return;
-
- /* If we hold a weak ref. on the persona's TpContact, release that. */
- if (persona.contact != null &&
- this._weakly_referenced_contacts.unset (persona.contact) == true)
- {
- persona.contact.weak_unref (this._contact_weak_notify_cb);
- }
-
- /*
- * remove all persona-keyed entries
- */
- foreach (var channel in this._channel_group_personas_map.keys)
- {
- var members = this._channel_group_personas_map[channel];
- if (members != null)
- members.remove (persona);
- }
-
- foreach (var name in this._group_outgoing_adds.keys)
- {
- var members = this._group_outgoing_adds[name];
- if (members != null)
- members.remove (persona);
- }
-
- var personas = new HashSet<Persona> ();
- personas.add (persona);
-
- this._emit_personas_changed (null, personas, message, actor, reason);
- this._personas.unset (persona.iid);
- this._persona_set.remove (persona);
+ tp_persona.contact.remove_from_group_async (group);
}
/**
@@ -1656,7 +1008,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
{
var tp_persona = (Tpf.Persona) persona;
- if (tp_persona.contact == this._self_contact &&
+ if (persona == this._self_persona &&
tp_persona.is_in_contact_list == false)
{
throw new PersonaStoreError.UNSUPPORTED_ON_USER (
@@ -1670,604 +1022,20 @@ public class Tpf.PersonaStore : Folks.PersonaStore
return;
}
- try
- {
- FolksTpLowlevel.channel_group_change_membership (this._stored,
- (Handle) tp_persona.contact.handle, false, null);
- }
- catch (GLib.Error e1)
- {
- warning (
- /* Translators: The first parameter is a contact identifier, the
- * second is a contact list identifier and the third is an error
- * message. */
- _("Failed to remove Telepathy contact â%sâ from â%sâ list: %s"),
- tp_persona.contact.identifier, "stored", e1.message);
- }
-
- try
- {
- FolksTpLowlevel.channel_group_change_membership (this._subscribe,
- (Handle) tp_persona.contact.handle, false, null);
- }
- catch (GLib.Error e2)
- {
- warning (
- /* Translators: The first parameter is a contact identifier, the
- * second is a contact list identifier and the third is an error
- * message. */
- _("Failed to remove Telepathy contact â%sâ from â%sâ list: %s"),
- tp_persona.contact.identifier, "subscribe", e2.message);
- }
-
- try
- {
- FolksTpLowlevel.channel_group_change_membership (this._publish,
- (Handle) tp_persona.contact.handle, false, null);
- }
- catch (GLib.Error e3)
- {
- warning (
- /* Translators: The first parameter is a contact identifier, the
- * second is a contact list identifier and the third is an error
- * message. */
- _("Failed to remove Telepathy contact â%sâ from â%sâ list: %s"),
- tp_persona.contact.identifier, "publish", e3.message);
- }
-
- /* the contact will be actually removed (and signaled) when we hear back
- * from the server */
- }
-
- /* Only non-group contact list channels should use create_personas == true,
- * since the exposed set of Personas are meant to be filtered by them */
- private async void _channel_group_pend_incoming_adds (Channel channel,
- Array<uint> adds,
- bool create_personas)
- {
- var adds_length = adds != null ? adds.length : 0;
- if (adds_length >= 1)
- {
- if (create_personas)
- {
- yield this._create_personas_from_channel_handles_async (channel,
- adds);
- }
-
- for (var i = 0; i < adds.length; i++)
- {
- var channel_handle = (Handle) adds.index (i);
- var contact_handle = channel.group_get_handle_owner (
- channel_handle);
-
- HashSet<uint>? contact_handles =
- this._channel_group_incoming_adds[channel];
- if (contact_handles == null)
- {
- contact_handles = new HashSet<uint> ();
- this._channel_group_incoming_adds[channel] =
- contact_handles;
- }
- contact_handles.add (contact_handle);
- }
- }
-
- this._channel_groups_add_new_personas ();
- }
-
- private void _channel_group_handle_incoming_removes (Channel channel,
- Array<uint> removes)
- {
- var removes_length = removes != null ? removes.length : 0;
- if (removes_length >= 1)
- {
- var members_removed = new GLib.List<Persona> ();
-
- HashSet<Persona> members = this._channel_group_personas_map[channel];
- if (members == null)
- members = new HashSet<Persona> ();
-
- for (var i = 0; i < removes.length; i++)
- {
- var channel_handle = (Handle) removes.index (i);
- var contact_handle = channel.group_get_handle_owner (
- channel_handle);
- var persona = this._handle_persona_map[contact_handle];
-
-
- if (persona != null)
- {
- members.remove (persona);
- members_removed.prepend (persona);
- }
- }
-
- this._channel_group_personas_map[channel] = members;
-
- var name = channel.get_identifier ();
- if (this._group_is_display_group (name) &&
- members_removed.length () > 0)
- {
- members_removed.reverse ();
- this.group_members_changed (name, null, members_removed);
- }
- }
- }
-
- private void _set_up_new_group_channel (Channel channel)
- {
- /* hold a ref to the channel here until it's ready, so it doesn't
- * disappear */
- this._group_channels_unready[channel.get_identifier ()] = channel;
-
- channel.notify["channel-ready"].connect ((s, p) =>
- {
- var c = (Channel) s;
- var name = c.get_identifier ();
-
- var existing_channel = this._groups[name];
- if (existing_channel != null)
- {
- /* Somehow, this group channel has already been set up. We have to
- * hold a reference to the existing group while unsetting it in
- * the group map so that unsetting it doesn't cause it to be
- * destroyed. If that were to happen, channel_invalidated_cb()
- * would remove it from the group map a second time, causing a
- * double unref. */
- existing_channel.ref ();
- this._groups.unset (name);
- existing_channel.unref ();
- }
-
- /* Drop all references before we set the new channel */
- existing_channel = null;
-
- this._groups[name] = c;
- this._group_channels_unready.unset (name);
-
- c.invalidated.connect (this._channel_invalidated_cb);
- c.group_members_changed_detailed.connect (
- this._channel_group_members_changed_detailed_cb);
-
- unowned Intset members = c.group_get_members ();
- if (members != null)
- {
- this._channel_group_pend_incoming_adds.begin (c,
- members.to_array (), false);
- }
- });
- }
-
- private void _disconnect_from_group_channel (Channel channel)
- {
- var name = channel.get_identifier ();
- debug ("Disconnecting from group channel '%s'.", name);
-
- channel.group_members_changed_detailed.disconnect (
- this._channel_group_members_changed_detailed_cb);
- channel.invalidated.disconnect (this._channel_invalidated_cb);
- }
-
- private void _channel_group_members_changed_detailed_cb (Channel channel,
- /* FIXME: Array<uint> => Array<Handle>; parser bug */
- Array<uint> added,
- Array<uint> removed,
- Array<uint> local_pending,
- Array<uint> remote_pending,
- HashTable details)
- {
- if (added != null)
- this._channel_group_pend_incoming_adds.begin (channel, added, false);
-
- if (removed != null)
- {
- this._channel_group_handle_incoming_removes (channel, removed);
- }
-
- /* FIXME: continue for the other arrays */
- }
-
- internal async void _change_group_membership (Folks.Persona persona,
- string group, bool is_member)
- {
- var tp_persona = (Tpf.Persona) persona;
- var channel = this._groups[group];
- var change_map = is_member ? this._group_outgoing_adds :
- this._group_outgoing_removes;
- var change_set = change_map[group];
-
- if (change_set == null)
- {
- change_set = new HashSet<Tpf.Persona> ();
- change_map[group] = change_set;
- }
- change_set.add (tp_persona);
-
- if (channel == null)
- {
- /* the changes queued above will be resolve in the NewChannels handler
- */
- FolksTpLowlevel.connection_create_group_async (this.account.connection,
- group);
- }
- else
- {
- /* the channel is already ready, so resolve immediately */
- this._channel_group_changes_resolve (channel);
- }
- }
-
- private void _change_standard_contact_list_membership (
- TelepathyGLib.Channel channel, Folks.Persona persona, bool is_member,
- string? message)
- {
- var tp_persona = (Tpf.Persona) persona;
-
- if (tp_persona.contact == null)
- {
- warning ("Skipping Tpf.Persona %p contact list change because it " +
- "has no attached TpContact", tp_persona);
- return;
- }
-
- try
- {
- FolksTpLowlevel.channel_group_change_membership (channel,
- (Handle) tp_persona.contact.handle, is_member, message);
- }
- catch (GLib.Error e)
- {
- if (is_member == true)
- {
- warning (
- /* Translators: The first parameter is a contact identifier,
- * the second is a contact list identifier and the third is an
- * error message. */
- _("Failed to add Telepathy contact â%sâ to â%sâ list: %s"),
- tp_persona.contact.identifier, channel.get_identifier (),
- e.message);
- }
- else
- {
- warning (
- /* Translators: The first parameter is a contact identifier,
- * the second is a contact list identifier and the third is an
- * error message. */
- _("Failed to remove Telepathy contact â%sâ from â%sâ list: %s"),
- tp_persona.contact.identifier, channel.get_identifier (),
- e.message);
- }
- }
+ tp_persona.contact.remove_async ();
}
- private async Channel? _add_standard_channel (Connection conn, string name)
+ private async Persona _ensure_persona_for_id (string contact_id)
+ throws GLib.Error
{
- Channel? channel = null;
-
- debug ("Adding standard channel '%s' to connection %p for " +
- "Tpf.PersonaStore %p ('%s').", name, conn, this, this.id);
-
- /* FIXME: handle the error GLib.Error from this function */
- try
- {
- channel =
- yield FolksTpLowlevel.connection_open_contact_list_channel_async (
- conn, name);
- }
- catch (GLib.Error e)
- {
- debug ("Failed to add channel '%s': %s", name, e.message);
-
- /* If the Connection doesn't support 'stored' channels we
- * pretend we've received the stored channel members.
- *
- * When this happens it probably means the ConnectionManager doesn't
- * implement the Channel.Type.ContactList interface.
- *
- * See: https://bugzilla.gnome.org/show_bug.cgi?id=656184 */
- this._got_stored_channel_members = true;
- this._notify_if_is_quiescent ();
-
- /* XXX: assuming there's no decent way to recover from this */
-
- return null;
- }
-
- this._set_up_new_standard_channel (channel);
-
- return channel;
- }
-
- /* FIXME: Array<uint> => Array<Handle>; parser bug */
- private async void _create_personas_from_channel_handles_async (
- Channel channel,
- Array<uint> channel_handles)
- {
- uint[] contact_handles = {};
- for (var i = 0; i < channel_handles.length; i++)
- {
- var channel_handle = (Handle) channel_handles.index (i);
- var contact_handle = channel.group_get_handle_owner (channel_handle);
- Persona? persona = this._handle_persona_map[contact_handle];
-
- if (persona == null)
- {
- contact_handles += contact_handle;
- }
- else
- {
- /* Mark the persona as having been seen in the contact list.
- * The persona might have originally been discovered by querying
- * the Telepathy connection's self-handle; in this case, its
- * is-in-contact-list property will originally be false, as a
- * contact could be exposed as the self-handle, but not actually
- * be in the user's contact list. */
- debug ("Setting is-in-contact-list for '%s' to true",
- persona.uid);
- persona.is_in_contact_list = true;
- }
- }
-
- try
- {
- if (contact_handles.length < 1)
- return;
-
- GLib.List<TelepathyGLib.Contact> contacts =
- yield FolksTpLowlevel.connection_get_contacts_by_handle_async (
- this._conn, contact_handles, (uint[]) _contact_features);
-
- if (contacts == null || contacts.length () < 1)
- return;
-
- var contacts_array = new TelepathyGLib.Contact[contacts.length ()];
- var j = 0;
- unowned GLib.List<TelepathyGLib.Contact> l = contacts;
- for (; l != null; l = l.next)
- {
- contacts_array[j] = l.data;
- j++;
- }
-
- this._add_new_personas_from_contacts (contacts_array);
- }
- catch (GLib.Error e)
- {
- warning (
- /* Translators: the first parameter is a channel identifier and
- * the second is an error message.. */
- _("Failed to create incoming Telepathy contacts from channel â%sâ: %s"),
- channel.get_identifier (), e.message);
- }
- }
-
- private async HashSet<Persona> _ensure_personas_from_contact_ids (
- string[] contact_ids) throws GLib.Error
- {
- var personas = new HashSet<Persona> ();
- var personas_added = new HashSet<Persona> ();
-
- if (contact_ids.length == 0)
- return personas;
+ var contact_ids = new string[1];
+ contact_ids[0] = contact_id;
GLib.List<TelepathyGLib.Contact> contacts =
yield FolksTpLowlevel.connection_get_contacts_by_id_async (
- this._conn, contact_ids, (uint[]) _contact_features);
-
- unowned GLib.List<TelepathyGLib.Contact> l;
- for (l = contacts; l != null; l = l.next)
- {
- var contact = l.data;
-
- debug ("Creating persona from contact '%s'", contact.identifier);
+ this._conn, contact_ids, {});
- bool added;
- var persona = this._add_persona_from_contact (contact, true, out added);
- if (added)
- personas_added.add (persona);
-
- personas.add (persona);
- }
-
- if (personas_added.size > 0)
- {
- this._emit_personas_changed (personas_added, null);
- }
-
- return personas;
- }
-
- private void _contact_weak_notify_cb (Object obj)
- {
- var c = obj as Contact;
- if (this._weakly_referenced_contacts != null)
- {
- Handle handle = this._weakly_referenced_contacts.get (c);
- this._weakly_referenced_contacts.unset (c);
-
- if (handle != 0)
- {
- this._ignore_by_handle ((!) handle, null, null,
- GroupDetails.ChangeReason.NONE);
- }
- }
- }
-
- internal Tpf.Persona? _ensure_persona_from_contact (Contact contact)
- {
- uint handle = contact.get_handle ();
-
- debug ("Ensuring contact %p (handle: %u) exists in Tpf.PersonaStore " +
- "%p ('%s').", contact, handle, this, this.id);
-
- if (handle == 0)
- {
- return null;
- }
-
- /* If the persona already exists, return them. */
- var persona = this._handle_persona_map[handle];
-
- if (persona != null)
- {
- return persona;
- }
-
- /* Otherwise, add the persona to the store. See bgo#665376 for details of
- * why this is necessary. Since the TpContact is coming from a source
- * other than the TpChannels which are associated with this store, we only
- * hold a weak reference to it and remove it from the store as soon as
- * it's destroyed. */
- bool added;
- persona = this._add_persona_from_contact (contact, false, out added);
-
- if (!added)
- {
- return null;
- }
-
- /* Weak ref. on the contact. */
- contact.weak_ref (this._contact_weak_notify_cb);
- this._weakly_referenced_contacts.set (contact, handle);
-
- /* Signal the addition of the new persona. */
- var personas = new HashSet<Persona> ();
- this._emit_personas_changed (personas, null);
-
- return persona;
- }
-
- private Tpf.Persona? _add_persona_from_contact (Contact contact,
- bool from_contact_list,
- out bool added)
- {
- var h = contact.get_handle ();
- Persona? persona = null;
-
- debug ("Adding persona from contact '%s'", contact.identifier);
-
- persona = this._handle_persona_map[h];
- if (persona == null)
- {
- persona = new Tpf.Persona (contact, this);
-
- this._personas.set (persona.iid, persona);
- this._persona_set.add (persona);
- this._handle_persona_map[h] = persona;
-
- /* If the handle is a favourite, ensure the persona's marked
- * as such. This deals with the case where we receive a
- * contact _after_ we've discovered that they're a
- * favourite. */
- persona.is_favourite = this._favourite_handles.contains (h);
-
- /* Only emit this debug message in the false case to reduce debug
- * spam (see https://bugzilla.gnome.org/show_bug.cgi?id=640901#c2). */
- if (from_contact_list == false)
- {
- debug (" Setting is-in-contact-list to false");
- }
-
- persona.is_in_contact_list = from_contact_list;
-
- added = true;
- }
- else
- {
- debug (" ...already exists.");
-
- /* Mark the persona as having been seen in the contact list.
- * The persona might have originally been discovered by querying
- * the Telepathy connection's self-handle; in this case, its
- * is-in-contact-list property will originally be false, as a
- * contact could be exposed as the self-handle, but not actually
- * be in the user's contact list. */
- if (persona.is_in_contact_list == false && from_contact_list == true)
- {
- debug (" Setting is-in-contact-list to true");
- persona.is_in_contact_list = true;
- }
-
- added = false;
- }
- return persona;
- }
-
- private void _add_new_personas_from_contacts (Contact[] contacts)
- {
- var personas = new HashSet<Persona> ();
-
- foreach (Contact contact in contacts)
- {
- bool added;
- var persona = this._add_persona_from_contact (contact, true, out added);
- if (added)
- personas.add (persona);
- }
-
- this._channel_groups_add_new_personas ();
-
- if (personas.size > 0)
- {
- this._emit_personas_changed (personas, null);
- }
- }
-
- private void _channel_groups_add_new_personas ()
- {
- foreach (var entry in this._channel_group_incoming_adds.entries)
- {
- var channel = (Channel) entry.key;
- var members_added = new GLib.List<Persona> ();
-
- HashSet<Persona> members = this._channel_group_personas_map[channel];
- if (members == null)
- members = new HashSet<Persona> ();
-
- debug ("Adding members to channel '%s':", channel.get_identifier ());
-
- var contact_handles = entry.value;
- if (contact_handles != null && contact_handles.size > 0)
- {
- var contact_handles_added = new HashSet<uint> ();
- foreach (var contact_handle in contact_handles)
- {
- var persona = this._handle_persona_map[contact_handle];
- if (persona != null)
- {
- debug (" %s", persona.uid);
- members.add (persona);
- members_added.prepend (persona);
- contact_handles_added.add (contact_handle);
- }
- }
-
- foreach (var handle in contact_handles_added)
- contact_handles.remove (handle);
- }
-
- if (members.size > 0)
- this._channel_group_personas_map[channel] = members;
-
- var name = channel.get_identifier ();
- if (this._group_is_display_group (name) &&
- members_added.length () > 0)
- {
- members_added.reverse ();
- this.group_members_changed (name, members_added, null);
- }
- }
- }
-
- private bool _group_is_display_group (string group)
- {
- for (var i = 0; i < this._undisplayed_groups.length; i++)
- {
- if (this._undisplayed_groups[i] == group)
- return false;
- }
-
- return true;
+ return this._ensure_persona_for_contact (contacts.data);
}
/**
@@ -2302,61 +1070,13 @@ public class Tpf.PersonaStore : Folks.PersonaStore
_("Cannot create a new Telepathy contact while offline."));
}
- var contact_ids = new string[1];
- contact_ids[0] = contact_id;
-
try
{
- var personas = yield this._ensure_personas_from_contact_ids (
- contact_ids);
-
- if (personas.size == 0)
- {
- /* the persona already existed */
- return null;
- }
- else if (personas.size == 1)
- {
- /* Get the first (and only) Persona */
- Persona persona = null;
- foreach (var p in personas)
- {
- persona = p;
- break;
- }
+ var persona = yield this._ensure_persona_for_id (contact_id);
+ var tp_persona = (Tpf.Persona) persona;
+ tp_persona.contact.request_subscription_async (add_message);
- if (this._subscribe != null)
- this._change_standard_contact_list_membership (this._subscribe,
- persona, true, add_message);
-
- if (this._publish != null)
- {
- var flags = this._publish.group_get_flags ();
- if ((flags & ChannelGroupFlags.CAN_ADD) ==
- ChannelGroupFlags.CAN_ADD)
- {
- this._change_standard_contact_list_membership (
- this._publish, persona, true, add_message);
- }
- }
-
- return persona;
- }
- else
- {
- /* We ignore the case of an empty list, as it just means the
- * contact was already in our roster */
- var num_personas = personas.size;
- var message =
- ngettext (
- /* Translators: the parameter is the number of personas
- * which were returned. */
- "Requested a single persona, but got %u persona back.",
- "Requested a single persona, but got %u personas back.",
- num_personas);
-
- throw new PersonaStoreError.CREATE_FAILED (message, num_personas);
- }
+ return persona;
}
catch (GLib.Error e)
{
@@ -2519,7 +1239,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
try
{
success =
- yield this.account.connection.set_contact_info_async (
+ yield this._conn.set_contact_info_async (
info_list);
}
catch (GLib.Error e)
diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala
index 282b35d..ea28e54 100644
--- a/backends/telepathy/lib/tpf-persona.vala
+++ b/backends/telepathy/lib/tpf-persona.vala
@@ -438,6 +438,15 @@ public class Tpf.Persona : Folks.Persona,
return changed;
}
+ private void _contact_groups_changed (string[] added, string[] removed)
+ {
+ foreach (var group in added)
+ this._change_group (group, true);
+
+ foreach (var group in removed)
+ this._change_group (group, false);
+ }
+
/**
* { inheritDoc}
*
@@ -706,30 +715,11 @@ public class Tpf.Persona : Folks.Persona,
});
this._contact_notify_contact_info ();
- ((Tpf.PersonaStore) this.store).group_members_changed.connect (
- (s, group, added, removed) =>
- {
- if (added.find (this) != null)
- this._change_group (group, true);
-
- if (removed.find (this) != null)
- this._change_group (group, false);
- });
-
- ((Tpf.PersonaStore) this.store).group_removed.connect (
- (s, group, error) =>
- {
- /* FIXME: Can't use
- * !(error is TelepathyGLib.DBusError.OBJECT_REMOVED) because the
- * GIR bindings don't annotate errors */
- if (error != null &&
- (error.domain != TelepathyGLib.dbus_errors_quark () ||
- error.code != TelepathyGLib.DBusError.OBJECT_REMOVED))
- {
- debug ("Group invalidated: %s", error.message);
- this._change_group (group, false);
- }
- });
+ this.contact.contact_groups_changed.connect ((added, removed) =>
+ {
+ this._contact_groups_changed (added, removed);
+ });
+ this._contact_groups_changed (this.contact.get_contact_groups (), {});
if (this.is_user)
{
@@ -1094,6 +1084,6 @@ public class Tpf.Persona : Folks.Persona,
}
var store = PersonaStore.dup_for_account (account);
- return store._ensure_persona_from_contact (contact);
+ return store._ensure_persona_for_contact (contact);
}
}
diff --git a/tests/lib/telepathy/contactlist/backend.c b/tests/lib/telepathy/contactlist/backend.c
index daf3806..826aa82 100644
--- a/tests/lib/telepathy/contactlist/backend.c
+++ b/tests/lib/telepathy/contactlist/backend.c
@@ -133,6 +133,7 @@ void
tp_tests_backend_set_up (TpTestsBackend *self)
{
TpTestsBackendPrivate *priv = self->priv;
+ TpSimpleClientFactory *factory;
GError *error = NULL;
/* Override the handler set in the general Folks.TestCase class */
@@ -158,6 +159,17 @@ tp_tests_backend_set_up (TpTestsBackend *self)
priv->account_manager);
priv->client_am = tp_account_manager_dup ();
+ factory = tp_proxy_get_factory (priv->client_am);
+ tp_simple_client_factory_add_contact_features_varargs (factory,
+ TP_CONTACT_FEATURE_ALIAS,
+ TP_CONTACT_FEATURE_AVATAR_DATA,
+ TP_CONTACT_FEATURE_AVATAR_TOKEN,
+ TP_CONTACT_FEATURE_CAPABILITIES,
+ TP_CONTACT_FEATURE_CLIENT_TYPES,
+ TP_CONTACT_FEATURE_PRESENCE,
+ TP_CONTACT_FEATURE_CONTACT_INFO,
+ TP_CONTACT_FEATURE_CONTACT_GROUPS,
+ TP_CONTACT_FEATURE_INVALID);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]