api,call: new JoinMultiparty() and LeaveMultiparty() methods
This commit is contained in:
106
cli/mmcli-call.c
106
cli/mmcli-call.c
@@ -50,6 +50,8 @@ static gboolean info_flag; /* set when no action found */
|
||||
static gboolean start_flag;
|
||||
static gboolean accept_flag;
|
||||
static gchar *deflect_str;
|
||||
static gboolean join_multiparty_flag;
|
||||
static gboolean leave_multiparty_flag;
|
||||
static gboolean hangup_flag;
|
||||
static gchar *dtmf_request;
|
||||
|
||||
@@ -66,6 +68,14 @@ static GOptionEntry entries[] = {
|
||||
"Deflect the incoming call",
|
||||
"[NUMBER]",
|
||||
},
|
||||
{ "join-multiparty", 0, 0, G_OPTION_ARG_NONE, &join_multiparty_flag,
|
||||
"Join multiparty call",
|
||||
NULL,
|
||||
},
|
||||
{ "leave-multiparty", 0, 0, G_OPTION_ARG_NONE, &leave_multiparty_flag,
|
||||
"Leave multiparty call",
|
||||
NULL,
|
||||
},
|
||||
{ "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag,
|
||||
"Hang up the call",
|
||||
NULL,
|
||||
@@ -105,6 +115,8 @@ mmcli_call_options_enabled (void)
|
||||
n_actions = (start_flag +
|
||||
accept_flag +
|
||||
!!deflect_str +
|
||||
join_multiparty_flag +
|
||||
leave_multiparty_flag +
|
||||
hangup_flag +
|
||||
!!dtmf_request);
|
||||
|
||||
@@ -261,6 +273,60 @@ deflect_ready (MMCall *call,
|
||||
mmcli_async_operation_done ();
|
||||
}
|
||||
|
||||
static void
|
||||
join_multiparty_process_reply (gboolean result,
|
||||
const GError *error)
|
||||
{
|
||||
if (!result) {
|
||||
g_printerr ("error: couldn't join multiparty call: '%s'\n",
|
||||
error ? error->message : "unknown error");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
g_print ("successfully joined multiparty call\n");
|
||||
}
|
||||
|
||||
static void
|
||||
join_multiparty_ready (MMCall *call,
|
||||
GAsyncResult *result,
|
||||
gpointer nothing)
|
||||
{
|
||||
gboolean operation_result;
|
||||
GError *error = NULL;
|
||||
|
||||
operation_result = mm_call_join_multiparty_finish (call, result, &error);
|
||||
join_multiparty_process_reply (operation_result, error);
|
||||
|
||||
mmcli_async_operation_done ();
|
||||
}
|
||||
|
||||
static void
|
||||
leave_multiparty_process_reply (gboolean result,
|
||||
const GError *error)
|
||||
{
|
||||
if (!result) {
|
||||
g_printerr ("error: couldn't leave multiparty call: '%s'\n",
|
||||
error ? error->message : "unknown error");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
g_print ("successfully left multiparty call\n");
|
||||
}
|
||||
|
||||
static void
|
||||
leave_multiparty_ready (MMCall *call,
|
||||
GAsyncResult *result,
|
||||
gpointer nothing)
|
||||
{
|
||||
gboolean operation_result;
|
||||
GError *error = NULL;
|
||||
|
||||
operation_result = mm_call_leave_multiparty_finish (call, result, &error);
|
||||
leave_multiparty_process_reply (operation_result, error);
|
||||
|
||||
mmcli_async_operation_done ();
|
||||
}
|
||||
|
||||
static void
|
||||
hangup_process_reply (gboolean result,
|
||||
const GError *error)
|
||||
@@ -357,6 +423,24 @@ get_call_ready (GObject *source,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to join multiparty call? */
|
||||
if (join_multiparty_flag) {
|
||||
mm_call_join_multiparty (ctx->call,
|
||||
ctx->cancellable,
|
||||
(GAsyncReadyCallback)join_multiparty_ready,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to leave multiparty call? */
|
||||
if (leave_multiparty_flag) {
|
||||
mm_call_leave_multiparty (ctx->call,
|
||||
ctx->cancellable,
|
||||
(GAsyncReadyCallback)leave_multiparty_ready,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to hangup the call? */
|
||||
if (hangup_flag) {
|
||||
mm_call_hangup (ctx->call,
|
||||
@@ -454,6 +538,28 @@ mmcli_call_run_synchronous (GDBusConnection *connection)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to join multiparty call? */
|
||||
if (join_multiparty_flag) {
|
||||
gboolean operation_result;
|
||||
|
||||
operation_result = mm_call_join_multiparty_sync (ctx->call,
|
||||
NULL,
|
||||
&error);
|
||||
join_multiparty_process_reply (operation_result, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to leave multiparty call? */
|
||||
if (leave_multiparty_flag) {
|
||||
gboolean operation_result;
|
||||
|
||||
operation_result = mm_call_leave_multiparty_sync (ctx->call,
|
||||
NULL,
|
||||
&error);
|
||||
leave_multiparty_process_reply (operation_result, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Requesting to hangup the call? */
|
||||
if (hangup_flag) {
|
||||
gboolean operation_result;
|
||||
|
@@ -1349,6 +1349,12 @@ mm_call_send_dtmf_sync
|
||||
mm_call_deflect
|
||||
mm_call_deflect_finish
|
||||
mm_call_deflect_sync
|
||||
mm_call_join_multiparty
|
||||
mm_call_join_multiparty_finish
|
||||
mm_call_join_multiparty_sync
|
||||
mm_call_leave_multiparty
|
||||
mm_call_leave_multiparty_finish
|
||||
mm_call_leave_multiparty_sync
|
||||
<SUBSECTION Standard>
|
||||
MMCallClass
|
||||
MMCallPrivate
|
||||
@@ -3264,6 +3270,12 @@ mm_gdbus_call_call_send_dtmf_sync
|
||||
mm_gdbus_call_call_deflect
|
||||
mm_gdbus_call_call_deflect_finish
|
||||
mm_gdbus_call_call_deflect_sync
|
||||
mm_gdbus_call_call_join_multiparty
|
||||
mm_gdbus_call_call_join_multiparty_finish
|
||||
mm_gdbus_call_call_join_multiparty_sync
|
||||
mm_gdbus_call_call_leave_multiparty
|
||||
mm_gdbus_call_call_leave_multiparty_finish
|
||||
mm_gdbus_call_call_leave_multiparty_sync
|
||||
<SUBSECTION Private>
|
||||
mm_gdbus_call_set_direction
|
||||
mm_gdbus_call_set_number
|
||||
@@ -3277,6 +3289,8 @@ mm_gdbus_call_complete_hangup
|
||||
mm_gdbus_call_complete_send_dtmf
|
||||
mm_gdbus_call_complete_start
|
||||
mm_gdbus_call_complete_deflect
|
||||
mm_gdbus_call_complete_join_multiparty
|
||||
mm_gdbus_call_complete_leave_multiparty
|
||||
mm_gdbus_call_interface_info
|
||||
mm_gdbus_call_override_properties
|
||||
mm_gdbus_call_emit_dtmf_received
|
||||
|
@@ -52,6 +52,33 @@
|
||||
<arg name="number" type="s" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
JoinMultiparty:
|
||||
|
||||
Join the currently held call into a single multiparty call with another
|
||||
already active call.
|
||||
|
||||
The calls will be flagged with the
|
||||
'<link linkend="gdbus-property-org-freedesktop-ModemManager1-Call.Multiparty">Multiparty</link>'
|
||||
property while they are part of the multiparty call.
|
||||
|
||||
Applicable only if state is <link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link>.
|
||||
-->
|
||||
<method name="JoinMultiparty" />
|
||||
|
||||
<!--
|
||||
LeaveMultiparty:
|
||||
|
||||
If this call is part of an ongoing multiparty call, detach it from the multiparty call,
|
||||
put the multiparty call on hold, and activate this one alone. This operation makes this
|
||||
call private again between both ends of the call.
|
||||
|
||||
Applicable only if state is <link linkend="MM-CALL-STATE-ACTIVE:CAPS"><constant>MM_CALL_STATE_ACTIVE</constant></link> or
|
||||
<link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link> and
|
||||
the call is a multiparty call.
|
||||
-->
|
||||
<method name="LeaveMultiparty"/>
|
||||
|
||||
<!--
|
||||
Hangup:
|
||||
|
||||
|
@@ -592,6 +592,156 @@ mm_call_deflect_sync (MMCall *self,
|
||||
error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* mm_call_join_multiparty_finish:
|
||||
* @self: A #MMCall.
|
||||
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_join_multiparty().
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Finishes an operation started with mm_call_join_multiparty().
|
||||
*
|
||||
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
|
||||
*/
|
||||
gboolean
|
||||
mm_call_join_multiparty_finish (MMCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
|
||||
|
||||
return mm_gdbus_call_call_join_multiparty_finish (MM_GDBUS_CALL (self), res, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_call_join_multiparty:
|
||||
* @self: A #MMCall.
|
||||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||||
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
|
||||
* @user_data: User data to pass to @callback.
|
||||
*
|
||||
* Synchronously requests to join this call into a multiparty call.
|
||||
*
|
||||
* When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
|
||||
* You can then call mm_call_join_multiparty_finish() to get the result of the operation.
|
||||
*
|
||||
* See mm_call_join_multiparty_sync() for the synchronous, blocking version of this method.
|
||||
*/
|
||||
void
|
||||
mm_call_join_multiparty (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (MM_IS_CALL (self));
|
||||
|
||||
mm_gdbus_call_call_join_multiparty (MM_GDBUS_CALL (self),
|
||||
cancellable,
|
||||
callback,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_call_join_multiparty_sync:
|
||||
* @self: A #MMCall.g
|
||||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Synchronously requests to join this call into a multiparty call.
|
||||
*
|
||||
* The calling thread is blocked until an incoming call is ready.
|
||||
* See mm_call_join_multiparty() for the asynchronous version of this method.
|
||||
*
|
||||
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
|
||||
*/
|
||||
gboolean
|
||||
mm_call_join_multiparty_sync (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
|
||||
|
||||
return mm_gdbus_call_call_join_multiparty_sync (MM_GDBUS_CALL (self),
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_call_leave_multiparty_finish:
|
||||
* @self: A #MMCall.
|
||||
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_leave_multiparty().
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Finishes an operation started with mm_call_leave_multiparty().
|
||||
*
|
||||
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
|
||||
*/
|
||||
gboolean
|
||||
mm_call_leave_multiparty_finish (MMCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
|
||||
|
||||
return mm_gdbus_call_call_leave_multiparty_finish (MM_GDBUS_CALL (self), res, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_call_leave_multiparty:
|
||||
* @self: A #MMCall.
|
||||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||||
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
|
||||
* @user_data: User data to pass to @callback.
|
||||
*
|
||||
* Synchronously requests to make this call private again by leaving the
|
||||
* multiparty call.
|
||||
*
|
||||
* When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
|
||||
* You can then call mm_call_leave_multiparty_finish() to get the result of the operation.
|
||||
*
|
||||
* See mm_call_leave_multiparty_sync() for the synchronous, blocking version of this method.
|
||||
*/
|
||||
void
|
||||
mm_call_leave_multiparty (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (MM_IS_CALL (self));
|
||||
|
||||
mm_gdbus_call_call_leave_multiparty (MM_GDBUS_CALL (self),
|
||||
cancellable,
|
||||
callback,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_call_leave_multiparty_sync:
|
||||
* @self: A #MMCall.
|
||||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Synchronously requests to make this call private again by leaving the
|
||||
* multiparty call.
|
||||
*
|
||||
* The calling thread is blocked until an incoming call is ready.
|
||||
* See mm_call_leave_multiparty() for the asynchronous version of this method.
|
||||
*
|
||||
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
|
||||
*/
|
||||
gboolean
|
||||
mm_call_leave_multiparty_sync (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
|
||||
|
||||
return mm_gdbus_call_call_leave_multiparty_sync (MM_GDBUS_CALL (self),
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
|
@@ -127,6 +127,31 @@ gboolean mm_call_deflect_sync (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void mm_call_join_multiparty (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean mm_call_join_multiparty_finish (MMCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
gboolean mm_call_join_multiparty_sync (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void mm_call_leave_multiparty (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean mm_call_leave_multiparty_finish (MMCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
gboolean mm_call_leave_multiparty_sync (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void mm_call_hangup (MMCall *self,
|
||||
GCancellable *cancellable,
|
||||
|
@@ -461,6 +461,157 @@ handle_deflect (MMBaseCall *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Join multiparty call (DBus call handling) */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *self;
|
||||
MMBaseModem *modem;
|
||||
GDBusMethodInvocation *invocation;
|
||||
} HandleJoinMultipartyContext;
|
||||
|
||||
static void
|
||||
handle_join_multiparty_context_free (HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->modem);
|
||||
g_object_unref (ctx->self);
|
||||
g_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_voice_join_multiparty_ready (MMIfaceModemVoice *modem,
|
||||
GAsyncResult *res,
|
||||
HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_iface_modem_voice_join_multiparty_finish (modem, res, &error))
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
else
|
||||
mm_gdbus_call_complete_join_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
|
||||
handle_join_multiparty_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_join_multiparty_auth_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_join_multiparty_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This action is provided in the Call API, but implemented in the Modem.Voice interface
|
||||
* logic, because the action affects not only one call object, but all call objects that
|
||||
* are part of the multiparty call. */
|
||||
mm_iface_modem_voice_join_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
|
||||
ctx->self,
|
||||
(GAsyncReadyCallback)modem_voice_join_multiparty_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_join_multiparty (MMBaseCall *self,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
HandleJoinMultipartyContext *ctx;
|
||||
|
||||
ctx = g_new0 (HandleJoinMultipartyContext, 1);
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
g_object_get (self,
|
||||
MM_BASE_CALL_MODEM, &ctx->modem,
|
||||
NULL);
|
||||
|
||||
mm_base_modem_authorize (ctx->modem,
|
||||
invocation,
|
||||
MM_AUTHORIZATION_VOICE,
|
||||
(GAsyncReadyCallback)handle_join_multiparty_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Leave multiparty call (DBus call handling) */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *self;
|
||||
MMBaseModem *modem;
|
||||
GDBusMethodInvocation *invocation;
|
||||
} HandleLeaveMultipartyContext;
|
||||
|
||||
static void
|
||||
handle_leave_multiparty_context_free (HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->modem);
|
||||
g_object_unref (ctx->self);
|
||||
g_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_voice_leave_multiparty_ready (MMIfaceModemVoice *modem,
|
||||
GAsyncResult *res,
|
||||
HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_iface_modem_voice_leave_multiparty_finish (modem, res, &error))
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
else
|
||||
mm_gdbus_call_complete_leave_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
|
||||
|
||||
handle_leave_multiparty_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_leave_multiparty_auth_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_leave_multiparty_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This action is provided in the Call API, but implemented in the Modem.Voice interface
|
||||
* logic, because the action affects not only one call object, but all call objects that
|
||||
* are part of the multiparty call. */
|
||||
mm_iface_modem_voice_leave_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
|
||||
ctx->self,
|
||||
(GAsyncReadyCallback)modem_voice_leave_multiparty_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_leave_multiparty (MMBaseCall *self,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
HandleLeaveMultipartyContext *ctx;
|
||||
|
||||
ctx = g_new0 (HandleLeaveMultipartyContext, 1);
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
g_object_get (self,
|
||||
MM_BASE_CALL_MODEM, &ctx->modem,
|
||||
NULL);
|
||||
|
||||
mm_base_modem_authorize (ctx->modem,
|
||||
invocation,
|
||||
MM_AUTHORIZATION_VOICE,
|
||||
(GAsyncReadyCallback)handle_leave_multiparty_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Hangup call (DBus call handling) */
|
||||
|
||||
@@ -709,6 +860,14 @@ call_dbus_export (MMBaseCall *self)
|
||||
"handle-deflect",
|
||||
G_CALLBACK (handle_deflect),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-join-multiparty",
|
||||
G_CALLBACK (handle_join_multiparty),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-leave-multiparty",
|
||||
G_CALLBACK (handle_leave_multiparty),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-hangup",
|
||||
G_CALLBACK (handle_hangup),
|
||||
|
@@ -1261,6 +1261,288 @@ handle_transfer (MmGdbusModemVoice *skeleton,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Leave one of the calls from the multiparty call */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *call;
|
||||
GList *other_calls;
|
||||
} LeaveMultipartyContext;
|
||||
|
||||
static void
|
||||
leave_multiparty_context_free (LeaveMultipartyContext *ctx)
|
||||
{
|
||||
g_list_free_full (ctx->other_calls, g_object_unref);
|
||||
g_object_unref (ctx->call);
|
||||
g_slice_free (LeaveMultipartyContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_leave_multiparty_foreach (MMBaseCall *call,
|
||||
LeaveMultipartyContext *ctx)
|
||||
{
|
||||
/* ignore call that is leaving */
|
||||
if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
|
||||
return;
|
||||
|
||||
/* ignore non-multiparty calls */
|
||||
if (!mm_base_call_get_multiparty (call))
|
||||
return;
|
||||
|
||||
/* ignore calls not currently ongoing */
|
||||
switch (mm_base_call_get_state (call)) {
|
||||
case MM_CALL_STATE_ACTIVE:
|
||||
case MM_CALL_STATE_HELD:
|
||||
ctx->other_calls = g_list_append (ctx->other_calls, g_object_ref (call));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
leave_multiparty_ready (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
LeaveMultipartyContext *ctx;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there is only one remaining call that was part of the multiparty, consider that
|
||||
* one also no longer part of any multiparty, and put it on hold right away */
|
||||
if (g_list_length (ctx->other_calls) == 1) {
|
||||
mm_base_call_set_multiparty (MM_BASE_CALL (ctx->other_calls->data), FALSE);
|
||||
mm_base_call_change_state (MM_BASE_CALL (ctx->other_calls->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
/* If there are still more than one calls in the multiparty, just change state of all
|
||||
* of them. */
|
||||
else {
|
||||
GList *l;
|
||||
|
||||
for (l = ctx->other_calls; l; l = g_list_next (l))
|
||||
mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
|
||||
/* The call that left would now be active */
|
||||
mm_base_call_set_multiparty (ctx->call, FALSE);
|
||||
mm_base_call_change_state (ctx->call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
LeaveMultipartyContext *ctx;
|
||||
MMCallList *list = NULL;
|
||||
MMCallState call_state;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* validate multiparty status */
|
||||
if (!mm_base_call_get_multiparty (call)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"this call is not part of a multiparty call");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
/* validate call state */
|
||||
call_state = mm_base_call_get_state (call);
|
||||
if ((call_state != MM_CALL_STATE_ACTIVE) && (call_state != MM_CALL_STATE_HELD)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"invalid call state (%s): must be either active or held",
|
||||
mm_call_state_get_string (call_state));
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty ||
|
||||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot leave multiparty: unsupported");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
|
||||
NULL);
|
||||
if (!list) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot leave multiparty: missing call list");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (LeaveMultipartyContext);
|
||||
ctx->call = g_object_ref (call);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) leave_multiparty_context_free);
|
||||
|
||||
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_leave_multiparty_foreach, ctx);
|
||||
g_object_unref (list);
|
||||
|
||||
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty (self,
|
||||
call,
|
||||
(GAsyncReadyCallback)leave_multiparty_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Join calls into a multiparty call */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *call;
|
||||
GList *all_calls;
|
||||
gboolean added;
|
||||
} JoinMultipartyContext;
|
||||
|
||||
static void
|
||||
join_multiparty_context_free (JoinMultipartyContext *ctx)
|
||||
{
|
||||
g_list_free_full (ctx->all_calls, g_object_unref);
|
||||
g_object_unref (ctx->call);
|
||||
g_slice_free (JoinMultipartyContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_join_multiparty_foreach (MMBaseCall *call,
|
||||
JoinMultipartyContext *ctx)
|
||||
{
|
||||
/* always add call that is being added */
|
||||
if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
|
||||
ctx->added = TRUE;
|
||||
|
||||
/* ignore calls not currently ongoing */
|
||||
switch (mm_base_call_get_state (call)) {
|
||||
case MM_CALL_STATE_ACTIVE:
|
||||
case MM_CALL_STATE_HELD:
|
||||
ctx->all_calls = g_list_append (ctx->all_calls, g_object_ref (call));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
join_multiparty_ready (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
JoinMultipartyContext *ctx;
|
||||
GList *l;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = ctx->all_calls; l; l = g_list_next (l)) {
|
||||
mm_base_call_set_multiparty (MM_BASE_CALL (l->data), TRUE);
|
||||
mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
JoinMultipartyContext *ctx;
|
||||
MMCallList *list = NULL;
|
||||
MMCallState call_state;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* validate multiparty status */
|
||||
if (mm_base_call_get_multiparty (call)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"this call is already part of a multiparty call");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
/* validate call state */
|
||||
call_state = mm_base_call_get_state (call);
|
||||
if (call_state != MM_CALL_STATE_HELD) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"invalid call state (%s): must be held",
|
||||
mm_call_state_get_string (call_state));
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty ||
|
||||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot join multiparty: unsupported");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
|
||||
NULL);
|
||||
if (!list) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot join multiparty: missing call list");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (JoinMultipartyContext);
|
||||
ctx->call = g_object_ref (call);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) join_multiparty_context_free);
|
||||
|
||||
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_join_multiparty_foreach, ctx);
|
||||
g_object_unref (list);
|
||||
|
||||
/* our logic makes sure that we would be adding the incoming call into the multiparty call */
|
||||
g_assert (ctx->added);
|
||||
|
||||
/* NOTE: we do not give the call we want to join, because the join operation acts on all
|
||||
* active/held calls. */
|
||||
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty (self,
|
||||
(GAsyncReadyCallback)join_multiparty_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* In-call setup operation
|
||||
*
|
||||
|
@@ -150,6 +150,23 @@ struct _MMIfaceModemVoice {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Join multiparty */
|
||||
void (* join_multiparty) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* join_multiparty_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Leave multiparty */
|
||||
void (* leave_multiparty) (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* leave_multiparty_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Transfer */
|
||||
void (* transfer) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
@@ -207,4 +224,25 @@ void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self,
|
||||
guint index,
|
||||
const gchar *dtmf);
|
||||
|
||||
/* Join/Leave multiparty calls
|
||||
*
|
||||
* These actions are provided in the Call API, but implemented in the
|
||||
* modem Voice interface because they really affect multiple calls at
|
||||
* the same time.
|
||||
*/
|
||||
void mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
#endif /* MM_IFACE_MODEM_VOICE_H */
|
||||
|
Reference in New Issue
Block a user