1 /*
2 * call-stream.h - high level API for Call streams
3 *
4 * Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * SECTION:call-stream
23 * @title: TpCallStream
24 * @short_description: proxy object for a call stream
25 *
26 * #TpCallStream is a sub-class of #TpProxy providing convenient API
27 * to represent #TpCallChannel's stream.
28 */
29
30 /**
31 * TpCallStream:
32 *
33 * Data structure representing a #TpCallStream.
34 *
35 * Since: 0.17.5
36 */
37
38 /**
39 * TpCallStreamClass:
40 *
41 * The class of a #TpCallStream.
42 *
43 * Since: 0.17.5
44 */
45
46 #include "config.h"
47
48 #include "telepathy-glib/call-stream.h"
49
50 #include <telepathy-glib/call-misc.h>
51 #include <telepathy-glib/call-content.h>
52 #include <telepathy-glib/dbus.h>
53 #include <telepathy-glib/enums.h>
54 #include <telepathy-glib/errors.h>
55 #include <telepathy-glib/gtypes.h>
56 #include <telepathy-glib/interfaces.h>
57 #include <telepathy-glib/proxy-subclass.h>
58 #include <telepathy-glib/util.h>
59
60 #define DEBUG_FLAG TP_DEBUG_CALL
61 #include "telepathy-glib/debug-internal.h"
62 #include "telepathy-glib/call-internal.h"
63 #include "telepathy-glib/proxy-internal.h"
64 #include "telepathy-glib/util-internal.h"
65
66 #include "_gen/tp-cli-call-stream-body.h"
67
68 G_DEFINE_TYPE (TpCallStream, tp_call_stream, TP_TYPE_PROXY)
69
70 struct _TpCallStreamPrivate
71 {
72 TpConnection *connection;
73
74 TpCallContent *content;
75
76 /* TpContact -> TpSendingState */
77 GHashTable *remote_members;
78 TpSendingState local_sending_state;
79 gboolean can_request_receiving;
80
81 gboolean properties_retrieved;
82 };
83
84 enum
85 {
86 PROP_CONNECTION = 1,
87 PROP_LOCAL_SENDING_STATE,
88 PROP_CAN_REQUEST_RECEIVING,
89 PROP_CONTENT
90 };
91
92 enum /* signals */
93 {
94 LOCAL_SENDING_STATE_CHANGED,
95 REMOTE_MEMBERS_CHANGED,
96 LAST_SIGNAL
97 };
98
99 static guint _signals[LAST_SIGNAL] = { 0, };
100
101 static void
update_remote_members(TpCallStream * self,GHashTable * updates,GPtrArray * removed)102 update_remote_members (TpCallStream *self,
103 GHashTable *updates,
104 GPtrArray *removed)
105 {
106 if (updates != NULL)
107 {
108 tp_g_hash_table_update (self->priv->remote_members, updates,
109 g_object_ref, NULL);
110 }
111
112 if (removed != NULL)
113 {
114 guint i;
115
116 for (i = 0; i < removed->len; i++)
117 {
118 g_hash_table_remove (self->priv->remote_members,
119 g_ptr_array_index (removed, i));
120 }
121 }
122 }
123
124 static void
remote_members_changed_cb(TpCallStream * self,GHashTable * updates,GHashTable * identifiers,const GArray * removed,const GValueArray * reason,gpointer user_data,GObject * weak_object)125 remote_members_changed_cb (TpCallStream *self,
126 GHashTable *updates,
127 GHashTable *identifiers,
128 const GArray *removed,
129 const GValueArray *reason,
130 gpointer user_data,
131 GObject *weak_object)
132 {
133 GHashTable *updates_contacts;
134 GPtrArray *removed_contacts;
135 TpCallStateReason *r;
136
137 if (!self->priv->properties_retrieved)
138 return;
139
140 DEBUG ("Remote members: %d updated, %d removed",
141 g_hash_table_size (updates), removed->len);
142
143 updates_contacts = _tp_call_members_convert_table (self->priv->connection,
144 updates, identifiers);
145 removed_contacts = _tp_call_members_convert_array (self->priv->connection,
146 removed);
147 r = _tp_call_state_reason_new (reason);
148
149 update_remote_members (self, updates_contacts, removed_contacts);
150
151 g_signal_emit (self, _signals[REMOTE_MEMBERS_CHANGED], 0,
152 updates_contacts, removed_contacts, r);
153
154 g_hash_table_unref (updates_contacts);
155 g_ptr_array_unref (removed_contacts);
156 _tp_call_state_reason_unref (r);
157 }
158
159 static void
local_sending_state_changed_cb(TpCallStream * self,guint state,const GValueArray * reason,gpointer user_data,GObject * weak_object)160 local_sending_state_changed_cb (TpCallStream *self,
161 guint state,
162 const GValueArray *reason,
163 gpointer user_data,
164 GObject *weak_object)
165 {
166 TpCallStateReason *r;
167
168 if (!self->priv->properties_retrieved)
169 return;
170
171 self->priv->local_sending_state = state;
172 g_object_notify (G_OBJECT (self), "local-sending-state");
173
174 r = _tp_call_state_reason_new (reason);
175 g_signal_emit (self, _signals[LOCAL_SENDING_STATE_CHANGED], 0,
176 self->priv->local_sending_state, r);
177 _tp_call_state_reason_unref (r);
178 }
179
180 static void
got_all_properties_cb(TpProxy * proxy,GHashTable * properties,const GError * error,gpointer user_data,GObject * weak_object)181 got_all_properties_cb (TpProxy *proxy,
182 GHashTable *properties,
183 const GError *error,
184 gpointer user_data,
185 GObject *weak_object)
186 {
187 TpCallStream *self = (TpCallStream *) proxy;
188 const gchar * const *interfaces;
189 GHashTable *remote_members;
190 GHashTable *identifiers;
191 GHashTable *contacts;
192
193 if (error != NULL)
194 {
195 DEBUG ("Could not get the call stream properties: %s", error->message);
196 _tp_proxy_set_feature_prepared (proxy,
197 TP_CALL_STREAM_FEATURE_CORE, FALSE);
198 return;
199 }
200
201 self->priv->properties_retrieved = TRUE;
202
203 interfaces = tp_asv_get_boxed (properties,
204 "Interfaces", G_TYPE_STRV);
205 remote_members = tp_asv_get_boxed (properties,
206 "RemoteMembers", TP_HASH_TYPE_CONTACT_SENDING_STATE_MAP),
207 identifiers = tp_asv_get_boxed (properties,
208 "RemoteMemberIdentifiers", TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP);
209 self->priv->local_sending_state = tp_asv_get_uint32 (properties,
210 "LocalSendingState", NULL);
211 self->priv->can_request_receiving = tp_asv_get_boolean (properties,
212 "CanRequestReceiving", NULL);
213
214 tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
215
216 contacts = _tp_call_members_convert_table (self->priv->connection,
217 remote_members, identifiers);
218 update_remote_members (self, contacts, NULL);
219 g_hash_table_unref (contacts);
220
221 _tp_proxy_set_feature_prepared (proxy, TP_CALL_STREAM_FEATURE_CORE, TRUE);
222 }
223
224 static void
tp_call_stream_constructed(GObject * obj)225 tp_call_stream_constructed (GObject *obj)
226 {
227 TpCallStream *self = (TpCallStream *) obj;
228
229 ((GObjectClass *) tp_call_stream_parent_class)->constructed (obj);
230
231 /* Connect signals for mutable properties */
232 tp_cli_call_stream_connect_to_remote_members_changed (self,
233 remote_members_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
234 tp_cli_call_stream_connect_to_local_sending_state_changed (self,
235 local_sending_state_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
236
237 tp_cli_dbus_properties_call_get_all (self, -1,
238 TP_IFACE_CALL_STREAM,
239 got_all_properties_cb, NULL, NULL, G_OBJECT (self));
240 }
241
242 static void
tp_call_stream_dispose(GObject * object)243 tp_call_stream_dispose (GObject *object)
244 {
245 TpCallStream *self = (TpCallStream *) object;
246
247 g_clear_object (&self->priv->content);
248 g_clear_object (&self->priv->connection);
249 tp_clear_pointer (&self->priv->remote_members, g_hash_table_unref);
250
251 G_OBJECT_CLASS (tp_call_stream_parent_class)->dispose (object);
252 }
253
254 static void
tp_call_stream_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)255 tp_call_stream_get_property (GObject *object,
256 guint property_id,
257 GValue *value,
258 GParamSpec *pspec)
259 {
260 TpCallStream *self = (TpCallStream *) object;
261 TpCallStreamPrivate *priv = self->priv;
262
263 switch (property_id)
264 {
265 case PROP_CONNECTION:
266 g_value_set_object (value, self->priv->connection);
267 break;
268 case PROP_LOCAL_SENDING_STATE:
269 g_value_set_uint (value, priv->local_sending_state);
270 break;
271 case PROP_CAN_REQUEST_RECEIVING:
272 g_value_set_boolean (value, priv->can_request_receiving);
273 break;
274 case PROP_CONTENT:
275 g_value_set_object (value, self->priv->content);
276 break;
277 default:
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
279 break;
280 }
281 }
282
283 static void
tp_call_stream_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)284 tp_call_stream_set_property (GObject *object,
285 guint property_id,
286 const GValue *value,
287 GParamSpec *pspec)
288 {
289 TpCallStream *self = (TpCallStream *) object;
290
291 switch (property_id)
292 {
293 case PROP_CONNECTION:
294 g_assert (self->priv->connection == NULL); /* construct-only */
295 self->priv->connection = g_value_dup_object (value);
296 break;
297 case PROP_CONTENT:
298 g_assert (self->priv->content == NULL); /* construct-only */
299 self->priv->content = g_value_dup_object (value);
300 break;
301 default:
302 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
303 break;
304 }
305 }
306
307 enum {
308 FEAT_CORE,
309 N_FEAT
310 };
311
312 static const TpProxyFeature *
tp_call_stream_list_features(TpProxyClass * cls G_GNUC_UNUSED)313 tp_call_stream_list_features (TpProxyClass *cls G_GNUC_UNUSED)
314 {
315 static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
316
317 if (G_LIKELY (features[0].name != 0))
318 return features;
319
320 /* started from constructed */
321 features[FEAT_CORE].name = TP_CALL_STREAM_FEATURE_CORE;
322 features[FEAT_CORE].core = TRUE;
323
324 /* assert that the terminator at the end is there */
325 g_assert (features[N_FEAT].name == 0);
326
327 return features;
328 }
329
330 static void
tp_call_stream_class_init(TpCallStreamClass * klass)331 tp_call_stream_class_init (TpCallStreamClass *klass)
332 {
333 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
334 TpProxyClass *proxy_class = (TpProxyClass *) klass;
335 GParamSpec *param_spec;
336
337 gobject_class->constructed = tp_call_stream_constructed;
338 gobject_class->get_property = tp_call_stream_get_property;
339 gobject_class->set_property = tp_call_stream_set_property;
340 gobject_class->dispose = tp_call_stream_dispose;
341
342 proxy_class->list_features = tp_call_stream_list_features;
343 proxy_class->interface = TP_IFACE_QUARK_CALL_STREAM;
344
345 g_type_class_add_private (gobject_class, sizeof (TpCallStreamPrivate));
346 tp_call_stream_init_known_interfaces ();
347
348 /**
349 * TpCallStream:connection:
350 *
351 * The #TpConnection of the call.
352 *
353 * Since: 0.17.5
354 */
355 param_spec = g_param_spec_object ("connection", "Connection",
356 "The connection of this stream",
357 TP_TYPE_CONNECTION,
358 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
359 g_object_class_install_property (gobject_class, PROP_CONNECTION,
360 param_spec);
361
362 /**
363 * TpCallStream:local-sending-state:
364 *
365 * The local user's sending state, from #TpSendingState.
366 *
367 * Since: 0.17.5
368 */
369 param_spec = g_param_spec_uint ("local-sending-state", "LocalSendingState",
370 "Local sending state",
371 0, G_MAXUINT, 0,
372 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
373 g_object_class_install_property (gobject_class, PROP_LOCAL_SENDING_STATE,
374 param_spec);
375
376 /**
377 * TpCallStream:can-request-receiving:
378 *
379 * If %TRUE, the user can request that a remote contact starts sending on this
380 * stream.
381 *
382 * Since: 0.17.5
383 */
384 param_spec = g_param_spec_boolean ("can-request-receiving",
385 "CanRequestReceiving",
386 "If true, the user can request that a remote contact starts sending on"
387 "this stream.",
388 FALSE,
389 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
390 g_object_class_install_property (gobject_class, PROP_CAN_REQUEST_RECEIVING,
391 param_spec);
392
393 /**
394 * TpCallStream:content:
395 *
396 * The Content that this streams belongs to
397 *
398 * Since: 0.17.6
399 */
400 param_spec = g_param_spec_object ("content",
401 "Content",
402 "The content that this Stream belongs to",
403 TP_TYPE_CALL_CONTENT,
404 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
405 g_object_class_install_property (gobject_class, PROP_CONTENT,
406 param_spec);
407
408 /**
409 * TpCallStream::local-sending-state-changed:
410 * @self: the #TpCallStream
411 * @state: the new #TpSendingState
412 * @reason: the #TpCallStateReason for the change
413 *
414 * The ::local-sending-state-changed signal is emitted whenever the
415 * stream sending state changes.
416 *
417 * Since: 0.17.5
418 */
419 _signals[LOCAL_SENDING_STATE_CHANGED] = g_signal_new ("local-sending-state-changed",
420 G_OBJECT_CLASS_TYPE (klass),
421 G_SIGNAL_RUN_LAST,
422 0, NULL, NULL, NULL,
423 G_TYPE_NONE,
424 2, G_TYPE_UINT, TP_TYPE_CALL_STATE_REASON);
425
426 /**
427 * TpCallStream::remote-members-changed:
428 * @self: the #TpCallStream
429 * @updates: (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
430 * #GHashTable mapping #TpContact to its new #TpSendingState
431 * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
432 * #GPtrArray of #TpContact removed from remote contacts
433 * @reason: the #TpCallStateReason for the change
434 *
435 * The ::remote-members-changed signal is emitted whenever the
436 * stream's remote members changes.
437 *
438 * It is NOT guaranteed that #TpContact objects have any feature prepared.
439 *
440 * Since: 0.17.5
441 */
442 _signals[REMOTE_MEMBERS_CHANGED] = g_signal_new ("remote-members-changed",
443 G_OBJECT_CLASS_TYPE (klass),
444 G_SIGNAL_RUN_LAST,
445 0, NULL, NULL, NULL,
446 G_TYPE_NONE,
447 3, G_TYPE_HASH_TABLE, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON);
448 }
449
450 static void
tp_call_stream_init(TpCallStream * self)451 tp_call_stream_init (TpCallStream *self)
452 {
453 self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_STREAM,
454 TpCallStreamPrivate);
455
456 self->priv->remote_members = g_hash_table_new_full (NULL, NULL,
457 g_object_unref, NULL);
458 }
459
460 /**
461 * tp_call_stream_init_known_interfaces:
462 *
463 * Ensure that the known interfaces for #TpCallStream have been set up.
464 * This is done automatically when necessary, but for correct
465 * overriding of library interfaces by local extensions, you should
466 * call this function before calling
467 * tp_proxy_or_subclass_hook_on_interface_add() with first argument
468 * %TP_TYPE_CALL_STREAM.
469 *
470 * Since: 0.17.5
471 */
472 void
tp_call_stream_init_known_interfaces(void)473 tp_call_stream_init_known_interfaces (void)
474 {
475 static gsize once = 0;
476
477 if (g_once_init_enter (&once))
478 {
479 GType tp_type = TP_TYPE_CALL_STREAM;
480
481 tp_proxy_init_known_interfaces ();
482 tp_proxy_or_subclass_hook_on_interface_add (tp_type,
483 tp_cli_call_stream_add_signals);
484 tp_proxy_subclass_add_error_mapping (tp_type,
485 TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR);
486
487 g_once_init_leave (&once, 1);
488 }
489 }
490
491 /**
492 * TP_CALL_STREAM_FEATURE_CORE:
493 *
494 * Expands to a call to a function that returns a quark for the "core"
495 * feature on a #TpCallStream.
496 *
497 * One can ask for a feature to be prepared using the tp_proxy_prepare_async()
498 * function, and waiting for it to trigger the callback.
499 */
500 GQuark
tp_call_stream_get_feature_quark_core(void)501 tp_call_stream_get_feature_quark_core (void)
502 {
503 return g_quark_from_static_string ("tp-call-stream-feature-core");
504 }
505
506 /**
507 * tp_call_stream_get_local_sending_state:
508 * @self: a #TpCallStream
509 *
510 * <!-- -->
511 *
512 * Returns: the value of #TpCallStream:local-sending-state
513 * Since: 0.17.5
514 */
515 TpSendingState
tp_call_stream_get_local_sending_state(TpCallStream * self)516 tp_call_stream_get_local_sending_state (TpCallStream *self)
517 {
518 g_return_val_if_fail (TP_IS_CALL_STREAM (self), TP_SENDING_STATE_NONE);
519
520 return self->priv->local_sending_state;
521 }
522
523 /**
524 * tp_call_stream_can_request_receiving:
525 * @self: a #TpCallStream
526 *
527 * <!-- -->
528 *
529 * Returns: the value of #TpCallStream:can-request-receiving
530 * Since: 0.17.5
531 */
532 gboolean
tp_call_stream_can_request_receiving(TpCallStream * self)533 tp_call_stream_can_request_receiving (TpCallStream *self)
534 {
535 g_return_val_if_fail (TP_IS_CALL_STREAM (self), FALSE);
536
537 return self->priv->can_request_receiving;
538 }
539
540 /**
541 * tp_call_stream_get_remote_members:
542 * @self: a #TpCallStream
543 *
544 * Get the remote contacts to who this stream is connected, mapped to their
545 * sending state.
546 *
547 * It is NOT guaranteed that #TpContact objects have any feature prepared.
548 *
549 * Returns: (transfer none) (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
550 * #GHashTable mapping #TpContact to its new #TpSendingState
551 * Since: 0.17.5
552 */
553 GHashTable *
tp_call_stream_get_remote_members(TpCallStream * self)554 tp_call_stream_get_remote_members (TpCallStream *self)
555 {
556 g_return_val_if_fail (TP_IS_CALL_STREAM (self), NULL);
557
558 return self->priv->remote_members;
559 }
560
561 static void
generic_async_cb(TpCallStream * self,const GError * error,gpointer user_data,GObject * weak_object)562 generic_async_cb (TpCallStream *self,
563 const GError *error,
564 gpointer user_data,
565 GObject *weak_object)
566 {
567 GSimpleAsyncResult *result = user_data;
568
569 if (error != NULL)
570 {
571 DEBUG ("Error: %s", error->message);
572 g_simple_async_result_set_from_error (result, error);
573 }
574
575 g_simple_async_result_complete (result);
576 }
577
578 /**
579 * tp_call_stream_set_sending_async:
580 * @self: a #TpCallStream
581 * @send: the requested sending state
582 * @callback: a callback to call when the operation finishes
583 * @user_data: data to pass to @callback
584 *
585 * Set the stream to start or stop sending media from the local user to other
586 * contacts.
587 *
588 * If @send is %TRUE, #TpCallStream:local-sending-state should change to
589 * %TP_SENDING_STATE_SENDING, if it isn't already.
590 * If @send is %FALSE, #TpCallStream:local-sending-state should change to
591 * %TP_SENDING_STATE_NONE, if it isn't already.
592 *
593 * Since: 0.17.5
594 */
595 void
tp_call_stream_set_sending_async(TpCallStream * self,gboolean send,GAsyncReadyCallback callback,gpointer user_data)596 tp_call_stream_set_sending_async (TpCallStream *self,
597 gboolean send,
598 GAsyncReadyCallback callback,
599 gpointer user_data)
600 {
601 GSimpleAsyncResult *result;
602
603 g_return_if_fail (TP_IS_CALL_STREAM (self));
604
605 result = g_simple_async_result_new (G_OBJECT (self), callback,
606 user_data, tp_call_stream_set_sending_async);
607
608 tp_cli_call_stream_call_set_sending (self, -1, send,
609 generic_async_cb, result, g_object_unref, G_OBJECT (self));
610 }
611
612 /**
613 * tp_call_stream_set_sending_finish:
614 * @self: a #TpCallStream
615 * @result: a #GAsyncResult
616 * @error: a #GError to fill
617 *
618 * Finishes tp_call_stream_set_sending_async().
619 *
620 * Since: 0.17.5
621 */
622 gboolean
tp_call_stream_set_sending_finish(TpCallStream * self,GAsyncResult * result,GError ** error)623 tp_call_stream_set_sending_finish (TpCallStream *self,
624 GAsyncResult *result,
625 GError **error)
626 {
627 _tp_implement_finish_void (self, tp_call_stream_set_sending_async);
628 }
629
630 /**
631 * tp_call_stream_request_receiving_async:
632 * @self: a #TpCallStream
633 * @contact: contact from which sending is requested
634 * @receive: the requested receiving state
635 * @callback: a callback to call when the operation finishes
636 * @user_data: data to pass to @callback
637 *
638 * Request that a remote contact stops or starts sending on this stream.
639 *
640 * The #TpCallStream:can-request-receiving property defines whether the protocol
641 * allows the local user to request the other side start sending on this stream.
642 *
643 * If @receive is %TRUE, request that the given contact starts to send media.
644 * If @receive is %FALSE, request that the given contact stops sending media.
645 *
646 * Since: 0.17.5
647 */
648 void
tp_call_stream_request_receiving_async(TpCallStream * self,TpContact * contact,gboolean receive,GAsyncReadyCallback callback,gpointer user_data)649 tp_call_stream_request_receiving_async (TpCallStream *self,
650 TpContact *contact,
651 gboolean receive,
652 GAsyncReadyCallback callback,
653 gpointer user_data)
654 {
655 GSimpleAsyncResult *result;
656
657 g_return_if_fail (TP_IS_CALL_STREAM (self));
658 g_return_if_fail (TP_IS_CONTACT (contact));
659 g_return_if_fail (tp_contact_get_connection (contact) ==
660 self->priv->connection);
661
662 result = g_simple_async_result_new (G_OBJECT (self), callback,
663 user_data, tp_call_stream_set_sending_async);
664
665 tp_cli_call_stream_call_request_receiving (self, -1,
666 tp_contact_get_handle (contact), receive,
667 generic_async_cb, result, g_object_unref, G_OBJECT (self));
668 }
669
670 /**
671 * tp_call_stream_request_receiving_finish:
672 * @self: a #TpCallStream
673 * @result: a #GAsyncResult
674 * @error: a #GError to fill
675 *
676 * Finishes tp_call_stream_request_receiving_async().
677 *
678 * Since: 0.17.5
679 */
680 gboolean
tp_call_stream_request_receiving_finish(TpCallStream * self,GAsyncResult * result,GError ** error)681 tp_call_stream_request_receiving_finish (TpCallStream *self,
682 GAsyncResult *result,
683 GError **error)
684 {
685 _tp_implement_finish_void (self, tp_call_stream_request_receiving_async);
686 }
687