summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-10-19 16:40:29 +0200
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-10-31 17:12:30 +0100
commit1b48e12c7ce5104d4266c6917f32add72aba427b (patch)
tree0d534be45156c03a30a334fae220c0c2557e5254
parent99faf245247dfd32ac21460b6e063655cdae2e60 (diff)
add TpConnection::blocked-contacts-changed: signal
https://bugs.freedesktop.org/show_bug.cgi?id=41801
-rw-r--r--telepathy-glib/connection-contact-list.c250
-rw-r--r--telepathy-glib/connection-internal.h4
-rw-r--r--telepathy-glib/connection.c40
-rw-r--r--tests/dbus/contact-list-clt.c73
4 files changed, 331 insertions, 36 deletions
diff --git a/telepathy-glib/connection-contact-list.c b/telepathy-glib/connection-contact-list.c
index 3363af19..952da068 100644
--- a/telepathy-glib/connection-contact-list.c
+++ b/telepathy-glib/connection-contact-list.c
@@ -1534,6 +1534,75 @@ tp_connection_get_feature_quark_contact_blocking (void)
return g_quark_from_static_string ("tp-connection-feature-contact-blocking");
}
+typedef struct
+{
+ /* TpHandle -> (const gchar *) identifier */
+ GHashTable *added;
+ /* TpHandle -> (const gchar *) identifier */
+ GHashTable *removed;
+
+ /* array of reffed TpContact */
+ GPtrArray *added_contacts;
+ /* array of reffed TpContact */
+ GPtrArray *removed_contacts;
+
+ GSimpleAsyncResult *result;
+} BlockedChangedItem;
+
+static BlockedChangedItem *
+blocked_changed_item_new (TpConnection *conn,
+ GHashTable *added,
+ GHashTable *removed,
+ GSimpleAsyncResult *result)
+{
+ BlockedChangedItem *item = g_slice_new0 (BlockedChangedItem);
+
+ item->added = g_hash_table_ref (added);
+
+ if (removed != NULL)
+ item->removed = g_hash_table_ref (removed);
+ else
+ item->removed = g_hash_table_new (NULL, NULL);
+
+ item->added_contacts = g_ptr_array_new_with_free_func (g_object_unref);
+ item->removed_contacts = g_ptr_array_new_with_free_func (g_object_unref);
+
+ if (result != NULL)
+ item->result = g_object_ref (result);
+
+ return item;
+}
+
+static void
+blocked_changed_item_free (BlockedChangedItem *item)
+{
+ g_hash_table_unref (item->added);
+ g_hash_table_unref (item->removed);
+ g_ptr_array_unref (item->added_contacts);
+ g_ptr_array_unref (item->removed_contacts);
+ g_clear_object (&item->result);
+
+ g_slice_free (BlockedChangedItem, item);
+}
+
+static void process_queued_blocked_changed (TpConnection *self);
+
+static void
+blocked_changed_head_ready (TpConnection *self)
+{
+ BlockedChangedItem *item;
+
+ item = g_queue_pop_head (self->priv->blocked_changed_queue);
+
+ if (item->result != NULL)
+ {
+ g_simple_async_result_complete (item->result);
+ }
+
+ blocked_changed_item_free (item);
+ process_queued_blocked_changed (self);
+}
+
static void
blocked_contacts_upgraded_cb (TpConnection *self,
guint n_contacts,
@@ -1542,8 +1611,11 @@ blocked_contacts_upgraded_cb (TpConnection *self,
gpointer user_data,
GObject *weak_object)
{
- GSimpleAsyncResult *result = user_data;
+ BlockedChangedItem *item;
guint i;
+ GPtrArray *added, *removed;
+
+ item = g_queue_peek_head (self->priv->blocked_changed_queue);
if (error != NULL)
{
@@ -1551,63 +1623,105 @@ blocked_contacts_upgraded_cb (TpConnection *self,
goto out;
}
+ added = g_ptr_array_new ();
+ removed = g_ptr_array_new_with_free_func (g_object_unref);
+
for (i = 0; i < n_contacts; i++)
{
- g_ptr_array_add (self->priv->blocked_contacts,
- g_object_ref (contacts[i]));
+ TpContact *contact = contacts[i];
+ TpHandle handle;
+
+ handle = tp_contact_get_handle (contact);
+
+ if (g_hash_table_lookup (item->added, GUINT_TO_POINTER (handle)) != NULL)
+ {
+ DEBUG ("Contact %s is blocked",
+ tp_contact_get_identifier (contact));
+
+ g_ptr_array_add (self->priv->blocked_contacts,
+ g_object_ref (contact));
+
+ g_ptr_array_add (added, contact);
+ }
+ else if (g_hash_table_lookup (item->removed,
+ GUINT_TO_POINTER (handle)) != NULL)
+ {
+ DEBUG ("Contact %s is no longer blocked",
+ tp_contact_get_identifier (contact));
+
+ /* Ref the contact as removing it from blocked_contacts may drop its
+ * last ref. */
+ g_ptr_array_add (removed, g_object_ref (contact));
+
+ g_ptr_array_remove (self->priv->blocked_contacts, contact);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
}
g_object_notify (G_OBJECT (self), "blocked-contacts");
+ g_signal_emit_by_name (self, "blocked-contacts-changed", added, removed);
+
+ g_ptr_array_unref (added);
+ g_ptr_array_unref (removed);
+
out:
- g_simple_async_result_complete (result);
+ blocked_changed_head_ready (self);
}
static void
-request_blocked_contacts_cb (TpConnection *self,
- GHashTable *contacts,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
+process_queued_blocked_changed (TpConnection *self)
{
- GSimpleAsyncResult *result = user_data;
+ BlockedChangedItem *item;
GHashTableIter iter;
gpointer key, value;
- GPtrArray *contacts_arr;
GArray *features;
+ GPtrArray *contacts;
- if (error != NULL)
+ item = g_queue_peek_head (self->priv->blocked_changed_queue);
+ if (item == NULL)
+ return;
+
+ /* contacts will contain the union of item->added_contacts and
+ * item->removed_contacts */
+ contacts = g_ptr_array_new ();
+
+ g_hash_table_iter_init (&iter, item->added);
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- DEBUG ("Error calling RequestBlockedContacts: %s", error->message);
- g_simple_async_result_set_from_error (result, error);
- g_simple_async_result_complete (result);
- return;
- }
+ TpHandle handle = GPOINTER_TO_UINT (key);
+ const gchar *identifier = value;
+ TpContact *contact;
- contacts_arr = g_ptr_array_new_with_free_func (g_object_unref);
+ contact = tp_simple_client_factory_ensure_contact (
+ tp_proxy_get_factory (self), self, handle, identifier);
+
+ /* TODO: set contact as blocked in TpContact */
+ g_ptr_array_add (item->added_contacts, contact);
+ g_ptr_array_add (contacts, contact);
+ }
- g_hash_table_iter_init (&iter, contacts);
+ g_hash_table_iter_init (&iter, item->removed);
while (g_hash_table_iter_next (&iter, &key, &value))
{
TpHandle handle = GPOINTER_TO_UINT (key);
- const gchar *id = value;
+ const gchar *identifier = value;
TpContact *contact;
- contact = tp_connection_dup_contact_if_possible (self, handle, id);
- if (contact == NULL)
- {
- DEBUG ("Failed to create contact %s (%d)", id, handle);
- continue;
- }
+ contact = tp_simple_client_factory_ensure_contact (
+ tp_proxy_get_factory (self), self, handle, identifier);
- g_ptr_array_add (contacts_arr, contact);
+ /* TODO: set contact as unblocked in TpContact */
+ g_ptr_array_add (item->removed_contacts, contact);
+ g_ptr_array_add (contacts, contact);
}
- if (contacts_arr->len == 0)
+ if (contacts->len == 0)
{
- /* No blocked contacts, we're done */
- g_simple_async_result_complete (result);
- g_ptr_array_unref (contacts_arr);
+ blocked_changed_head_ready (self);
return;
}
@@ -1615,13 +1729,52 @@ request_blocked_contacts_cb (TpConnection *self,
tp_proxy_get_factory (self), self);
tp_connection_upgrade_contacts (self,
- contacts_arr->len, (TpContact **) contacts_arr->pdata,
+ contacts->len, (TpContact **) contacts->pdata,
features->len, (TpContactFeature *) features->data,
- blocked_contacts_upgraded_cb,
- g_object_ref (result), g_object_unref, G_OBJECT (self));
+ blocked_contacts_upgraded_cb, NULL, NULL, NULL);
g_array_unref (features);
- g_ptr_array_unref (contacts_arr);
+}
+
+static void
+add_to_blocked_changed_queue (TpConnection *self,
+ GHashTable *added,
+ GHashTable *removed,
+ GSimpleAsyncResult *result)
+{
+ BlockedChangedItem *item;
+
+ item = blocked_changed_item_new (self, added, removed, result);
+ g_queue_push_tail (self->priv->blocked_changed_queue, item);
+
+ if (self->priv->blocked_changed_queue->length == 1)
+ process_queued_blocked_changed (self);
+}
+
+static void
+request_blocked_contacts_cb (TpConnection *self,
+ GHashTable *contacts,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ GSimpleAsyncResult *result = user_data;
+
+ self->priv->blocked_contacts_fetched = TRUE;
+
+ if (error != NULL)
+ {
+ DEBUG ("Error calling RequestBlockedContacts: %s", error->message);
+ g_simple_async_result_set_from_error (result, error);
+ g_simple_async_result_complete (result);
+ return;
+ }
+
+ /* We are not supposed to add items to this queue while the blocked contacts
+ * have been fetched. */
+ g_assert_cmpuint (self->priv->blocked_changed_queue->length, ==, 0);
+
+ add_to_blocked_changed_queue (self, contacts, NULL, result);
}
static void
@@ -1655,6 +1808,19 @@ out:
g_object_unref, G_OBJECT (self));
}
+static void
+blocked_contacts_changed_cb (TpConnection *self,
+ GHashTable *blocked,
+ GHashTable *unblocked,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ if (!self->priv->blocked_contacts_fetched)
+ return;
+
+ add_to_blocked_changed_queue (self, blocked, unblocked, NULL);
+}
+
void
_tp_connection_prepare_contact_blocking_async (TpProxy *proxy,
const TpProxyFeature *feature,
@@ -1663,6 +1829,7 @@ _tp_connection_prepare_contact_blocking_async (TpProxy *proxy,
{
TpConnection *self = (TpConnection *) proxy;
GSimpleAsyncResult *result;
+ GError *error = NULL;
result = g_simple_async_result_new ((GObject *) self, callback, user_data,
_tp_connection_prepare_contact_blocking_async);
@@ -1670,6 +1837,13 @@ _tp_connection_prepare_contact_blocking_async (TpProxy *proxy,
tp_cli_dbus_properties_call_get_all (self, -1,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING,
prepare_contact_blocking_cb, result, g_object_unref, NULL);
+
+ if (tp_cli_connection_interface_contact_blocking_connect_to_blocked_contacts_changed (self,
+ blocked_contacts_changed_cb, NULL, NULL, NULL, &error) == NULL)
+ {
+ DEBUG ("Failed to connect to BlockedContactsChanged: %s", error->message);
+ g_error_free (error);
+ }
}
/**
@@ -1705,3 +1879,9 @@ tp_connection_get_blocked_contacts (TpConnection *self)
{
return self->priv->blocked_contacts;
}
+
+void _tp_connection_blocked_changed_queue_free (GQueue *queue)
+{
+ g_queue_foreach (queue, (GFunc) blocked_changed_item_free, NULL);
+ g_queue_free (queue);
+}
diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h
index 04648df7..24031cde 100644
--- a/telepathy-glib/connection-internal.h
+++ b/telepathy-glib/connection-internal.h
@@ -92,10 +92,13 @@ struct _TpConnectionPrivate {
TpContactMetadataStorageType group_storage;
GPtrArray *contact_groups;
gboolean groups_fetched;
+ /* Queue of owned BlockedChangedItem */
+ GQueue *blocked_changed_queue;
/* ContactBlocking properies */
TpContactBlockingCapabilities contact_blocking_capabilities;
GPtrArray *blocked_contacts;
+ gboolean blocked_contacts_fetched;
TpProxyPendingCall *introspection_call;
@@ -168,6 +171,7 @@ void _tp_connection_prepare_contact_groups_async (TpProxy *proxy,
GAsyncReadyCallback callback,
gpointer user_data);
void _tp_connection_contacts_changed_queue_free (GQueue *queue);
+void _tp_connection_blocked_changed_queue_free (GQueue *queue);
void _tp_connection_prepare_contact_blocking_async (TpProxy *proxy,
const TpProxyFeature *feature,
diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c
index 46ed3275..aed21ea1 100644
--- a/telepathy-glib/connection.c
+++ b/telepathy-glib/connection.c
@@ -292,6 +292,7 @@ enum {
SIGNAL_GROUPS_REMOVED,
SIGNAL_GROUP_RENAMED,
SIGNAL_CONTACT_LIST_CHANGED,
+ SIGNAL_BLOCKED_CONTACTS_CHANGED,
N_SIGNALS
};
@@ -1464,6 +1465,8 @@ tp_connection_init (TpConnection *self)
self->priv->blocked_contacts = g_ptr_array_new_with_free_func (
g_object_unref);
+
+ self->priv->blocked_changed_queue = g_queue_new ();
}
static void
@@ -1548,6 +1551,8 @@ tp_connection_dispose (GObject *object)
tp_clear_pointer (&self->priv->roster, g_hash_table_unref);
tp_clear_pointer (&self->priv->contacts_changed_queue,
_tp_connection_contacts_changed_queue_free);
+ tp_clear_pointer (&self->priv->blocked_changed_queue,
+ _tp_connection_blocked_changed_queue_free);
if (self->priv->contacts != NULL)
{
@@ -2112,7 +2117,8 @@ tp_connection_class_init (TpConnectionClass *klass)
/**
* TpConnection:blocked-contacts:
*
- * A #GPtrArray of blocked #TpContact.
+ * A #GPtrArray of blocked #TpContact. Changes are notified using the
+ * #TpConnection::blocked-contacts-changed signal.
*
* For this property to be valid, you must first call
* tp_proxy_prepare_async() with the feature
@@ -2243,6 +2249,38 @@ tp_connection_class_init (TpConnectionClass *klass)
NULL, NULL,
_tp_marshal_VOID__BOXED_BOXED,
G_TYPE_NONE, 2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
+
+ /**
+ * TpConnection::blocked-contacts-changed:
+ * @self: a #TpConnection
+ * @added: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
+ * a #GPtrArray of #TpContact which have been blocked
+ * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
+ * a #GPtrArray of #TpContact which are no longer blocked
+ *
+ * Notify of changes in #TpConnection:blocked-contacts.
+ * It is guaranteed that all contacts have desired features prepared. See
+ * tp_simple_client_factory_add_contact_features() to define which features
+ * needs to be prepared.
+ *
+ * This signal is also emitted for the initial set of blocked contacts once
+ * retrieved.
+ *
+ * For this signal to be emitted, you must first call
+ * tp_proxy_prepare_async() with the feature
+ * %TP_CONNECTION_FEATURE_CONTACT_BLOCKING.
+ *
+ * Since: 0.UNRELEASED
+ */
+ signals[SIGNAL_BLOCKED_CONTACTS_CHANGED] = g_signal_new (
+ "blocked-contacts-changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _tp_marshal_VOID__BOXED_BOXED,
+ G_TYPE_NONE, 2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
+
}
/**
diff --git a/tests/dbus/contact-list-clt.c b/tests/dbus/contact-list-clt.c
index 2bf6926e..fbef64c4 100644
--- a/tests/dbus/contact-list-clt.c
+++ b/tests/dbus/contact-list-clt.c
@@ -29,6 +29,9 @@ typedef struct {
TpTextChannel *channel;
TpTextChannel *sms_channel;
+ GPtrArray *blocked_added;
+ GPtrArray *blocked_removed;
+
GError *error /* initialized where needed */;
gint wait;
} Test;
@@ -88,6 +91,9 @@ teardown (Test *test,
g_object_unref (test->connection);
g_object_unref (test->base_connection);
+
+ tp_clear_pointer (&test->blocked_added, g_ptr_array_unref);
+ tp_clear_pointer (&test->blocked_removed, g_ptr_array_unref);
}
static void
@@ -213,11 +219,30 @@ test_can_report_abusive (Test *test,
}
static void
+blocked_contacts_changed_cb (TpConnection *conn,
+ GPtrArray *added,
+ GPtrArray *removed,
+ Test *test)
+{
+ tp_clear_pointer (&test->blocked_added, g_ptr_array_unref);
+ tp_clear_pointer (&test->blocked_removed, g_ptr_array_unref);
+
+ test->blocked_added = g_ptr_array_ref (added);
+ test->blocked_removed = g_ptr_array_ref (removed);
+
+ test->wait--;
+ if (test->wait <= 0)
+ g_main_loop_quit (test->mainloop);
+}
+
+static void
test_blocked_contacts (Test *test,
gconstpointer data G_GNUC_UNUSED)
{
GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_BLOCKING, 0 };
GPtrArray *blocked;
+ TpHandle handle;
+ TpContact *alice;
/* Feature is not prepared yet */
g_object_get (test->connection, "blocked-contacts", &blocked, NULL);
@@ -242,6 +267,54 @@ test_blocked_contacts (Test *test,
blocked = tp_connection_get_blocked_contacts (test->connection);
g_assert_cmpuint (blocked->len, == , 2);
+
+ /* Let's block another contact */
+ handle = tp_handle_ensure (test->contact_repo, "alice", NULL, &test->error);
+ g_assert_no_error (test->error);
+
+ alice = tp_connection_dup_contact_if_possible (test->connection, handle,
+ "alice");
+ g_assert (alice != NULL);
+
+ g_signal_connect (test->connection, "blocked-contacts-changed",
+ G_CALLBACK (blocked_contacts_changed_cb), test);
+
+ tp_connection_block_contacts_async (test->connection,
+ 1, &alice, FALSE, block_contacts_cb, test);
+
+ g_object_unref (alice);
+
+ test->wait = 2;
+ g_main_loop_run (test->mainloop);
+ g_assert_no_error (test->error);
+
+ g_assert_cmpuint (test->blocked_added->len, ==, 1);
+ g_assert_cmpuint (test->blocked_removed->len, ==, 0);
+
+ alice = g_ptr_array_index (test->blocked_added, 0);
+ g_assert (TP_IS_CONTACT (alice));
+ g_assert_cmpstr (tp_contact_get_identifier (alice), ==, "alice");
+
+ blocked = tp_connection_get_blocked_contacts (test->connection);
+ g_assert_cmpuint (blocked->len, == , 3);
+
+ /* Cool, now unblock the poor Alice */
+ tp_connection_unblock_contacts_async (test->connection,
+ 1, &alice, unblock_contacts_cb, test);
+
+ test->wait = 2;
+ g_main_loop_run (test->mainloop);
+ g_assert_no_error (test->error);
+
+ g_assert_cmpuint (test->blocked_added->len, ==, 0);
+ g_assert_cmpuint (test->blocked_removed->len, ==, 1);
+
+ alice = g_ptr_array_index (test->blocked_removed, 0);
+ g_assert (TP_IS_CONTACT (alice));
+ g_assert_cmpstr (tp_contact_get_identifier (alice), ==, "alice");
+
+ blocked = tp_connection_get_blocked_contacts (test->connection);
+ g_assert_cmpuint (blocked->len, == , 2);
}
int