diff options
author | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-10-19 16:40:29 +0200 |
---|---|---|
committer | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-10-31 17:12:30 +0100 |
commit | 1b48e12c7ce5104d4266c6917f32add72aba427b (patch) | |
tree | 0d534be45156c03a30a334fae220c0c2557e5254 | |
parent | 99faf245247dfd32ac21460b6e063655cdae2e60 (diff) |
add TpConnection::blocked-contacts-changed: signal
https://bugs.freedesktop.org/show_bug.cgi?id=41801
-rw-r--r-- | telepathy-glib/connection-contact-list.c | 250 | ||||
-rw-r--r-- | telepathy-glib/connection-internal.h | 4 | ||||
-rw-r--r-- | telepathy-glib/connection.c | 40 | ||||
-rw-r--r-- | tests/dbus/contact-list-clt.c | 73 |
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 |