api,voice: new HoldAndAccept() method

This method will put the currently active call on hold, and right away
accept the next available call.

The user of the API does not need to specify explicitly which is the
next call to accept, because that is decided automatically:
  * If there is any waiting call, it will accept it right away.
  * If there is no waiting call but there is a held call, it will make
  the held call active again.
This commit is contained in:
Aleksander Morgado
2019-07-11 23:20:59 +02:00
parent 5de3c4893f
commit c713c2c5f9
7 changed files with 317 additions and 0 deletions
+49
View File
@@ -50,6 +50,7 @@ static Context *ctx;
static gboolean list_flag;
static gchar *create_str;
static gchar *delete_str;
static gboolean hold_and_accept_flag;
static gboolean hangup_and_accept_flag;
static GOptionEntry entries[] = {
@@ -65,6 +66,10 @@ static GOptionEntry entries[] = {
"Delete a call from a given modem",
"[PATH|INDEX]"
},
{ "voice-hold-and-accept", 0, 0, G_OPTION_ARG_NONE, &hold_and_accept_flag,
"Places all active calls in hold and accepts the next waiting or held call",
NULL
},
{ "voice-hangup-and-accept", 0, 0, G_OPTION_ARG_NONE, &hangup_and_accept_flag,
"Hangs up all active calls and accepts the next waiting or held call",
NULL
@@ -99,6 +104,7 @@ mmcli_modem_voice_options_enabled (void)
n_actions = (list_flag +
!!create_str +
!!delete_str +
hold_and_accept_flag +
hangup_and_accept_flag);
if (n_actions > 1) {
@@ -206,6 +212,31 @@ hangup_and_accept_ready (MMModemVoice *modem,
mmcli_async_operation_done ();
}
static void
hold_and_accept_process_reply (const GError *error)
{
if (error) {
g_printerr ("error: couldn't hold and accept: '%s'\n",
error->message);
exit (EXIT_FAILURE);
}
g_print ("operation successful\n");
}
static void
hold_and_accept_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
GError *error = NULL;
mm_modem_voice_hold_and_accept_finish (modem, result, &error);
hold_and_accept_process_reply (error);
mmcli_async_operation_done ();
}
static void
list_process_reply (GList *result,
const GError *error)
@@ -365,6 +396,16 @@ get_modem_ready (GObject *source,
return;
}
/* Request to hold and accept? */
if (hold_and_accept_flag) {
g_debug ("Asynchronously holding and accepting next call...");
mm_modem_voice_hold_and_accept (ctx->modem_voice,
ctx->cancellable,
(GAsyncReadyCallback)hold_and_accept_ready,
NULL);
return;
}
/* Request to hangup and accept? */
if (hangup_and_accept_flag) {
g_debug ("Asynchronously hanging up and accepting next call...");
@@ -471,6 +512,14 @@ mmcli_modem_voice_run_synchronous (GDBusConnection *connection)
return;
}
/* Request to hold and accept? */
if (hold_and_accept_flag) {
g_debug ("Synchronously holding and accepting call...");
mm_modem_voice_hold_and_accept_sync (ctx->modem_voice, NULL, &error);
hold_and_accept_process_reply (error);
return;
}
/* Request to hangup and accept? */
if (hangup_and_accept_flag) {
g_debug ("Synchronously hanging up and accepting call...");
@@ -1015,6 +1015,9 @@ mm_modem_voice_list_calls_sync
mm_modem_voice_hangup_and_accept
mm_modem_voice_hangup_and_accept_finish
mm_modem_voice_hangup_and_accept_sync
mm_modem_voice_hold_and_accept
mm_modem_voice_hold_and_accept_finish
mm_modem_voice_hold_and_accept_sync
<SUBSECTION Standard>
MMModemVoiceClass
MMModemVoicePrivate
@@ -2841,6 +2844,9 @@ mm_gdbus_modem_voice_call_list_calls_sync
mm_gdbus_modem_voice_call_hangup_and_accept
mm_gdbus_modem_voice_call_hangup_and_accept_finish
mm_gdbus_modem_voice_call_hangup_and_accept_sync
mm_gdbus_modem_voice_call_hold_and_accept
mm_gdbus_modem_voice_call_hold_and_accept_finish
mm_gdbus_modem_voice_call_hold_and_accept_sync
<SUBSECTION Private>
mm_gdbus_modem_voice_set_calls
mm_gdbus_modem_voice_emit_call_added
@@ -2849,6 +2855,7 @@ mm_gdbus_modem_voice_complete_create_call
mm_gdbus_modem_voice_complete_delete_call
mm_gdbus_modem_voice_complete_list_calls
mm_gdbus_modem_voice_complete_hangup_and_accept
mm_gdbus_modem_voice_complete_hold_and_accept
mm_gdbus_modem_voice_interface_info
mm_gdbus_modem_voice_override_properties
<SUBSECTION Standard>
@@ -63,6 +63,23 @@
<arg name="path" type="o" direction="out" />
</method>
<!--
HoldAndAccept:
Place all active calls on hold, if any, and accept the next
call.
Waiting calls have preference over held calls, so the next
call being active will be any waiting call, or otherwise,
any held call.
The user should monitor the state of all available ongoing
calls to be reported of which one becomes active.
No error is returned if there are no waiting or held calls.
-->
<method name="HoldAndAccept" />
<!--
HangupAndAccept:
+79
View File
@@ -532,6 +532,85 @@ mm_modem_voice_delete_call_sync (MMModemVoice *self,
/*****************************************************************************/
/**
* mm_modem_voice_hold_and_accept_finish:
* @self: A #MMModemVoice.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_hold_and_accept().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_voice_hold_and_accept().
*
* Returns: %TRUE if the operation was successful, %FALSE if @error is set.
* Since: 1.12
*/
gboolean
mm_modem_voice_hold_and_accept_finish (MMModemVoice *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
return mm_gdbus_modem_voice_call_hold_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
}
/**
* mm_modem_voice_hold_and_accept:
* @self: A #MMModemVoice.
* @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.
*
* Asynchronously puts all active calls on hold and accepts the next waiting or held 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_modem_voice_hold_and_accept_finish() to get the result of the operation.
*
* See mm_modem_voice_hold_and_accept_sync() for the synchronous, blocking version of this method.
*
* Since: 1.12
*/
void
mm_modem_voice_hold_and_accept (MMModemVoice *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_VOICE (self));
mm_gdbus_modem_voice_call_hold_and_accept (MM_GDBUS_MODEM_VOICE (self),
cancellable,
callback,
user_data);
}
/**
* mm_modem_voice_hold_and_accept_sync:
* @self: A #MMModemVoice.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously puts all active calls on hold and accepts the next waiting or held call.
*
* The calling thread is blocked until a reply is received. See mm_modem_voice_hold_and_accept()
* for the asynchronous version of this method.
*
* Returns: %TRUE if the operation was successful, %FALSE if @error is set.
* Since: 1.12
*/
gboolean
mm_modem_voice_hold_and_accept_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
return mm_gdbus_modem_voice_call_hold_and_accept_sync (MM_GDBUS_MODEM_VOICE (self),
cancellable,
error);
}
/*****************************************************************************/
/**
* mm_modem_voice_hangup_and_accept_finish:
* @self: A #MMModemVoice.
+11
View File
@@ -110,6 +110,17 @@ gboolean mm_modem_voice_delete_call_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error);
void mm_modem_voice_hold_and_accept (MMModemVoice *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_modem_voice_hold_and_accept_finish (MMModemVoice *self,
GAsyncResult *res,
GError **error);
gboolean mm_modem_voice_hold_and_accept_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error);
void mm_modem_voice_hangup_and_accept (MMModemVoice *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
+146
View File
@@ -544,6 +544,148 @@ handle_list (MmGdbusModemVoice *skeleton,
/*****************************************************************************/
typedef struct {
MmGdbusModemVoice *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemVoice *self;
GList *active_calls;
MMBaseCall *next_call;
} HandleHoldAndAcceptContext;
static void
handle_hold_and_accept_context_free (HandleHoldAndAcceptContext *ctx)
{
g_list_free_full (ctx->active_calls, g_object_unref);
g_clear_object (&ctx->next_call);
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_slice_free (HandleHoldAndAcceptContext, ctx);
}
static void
hold_and_accept_ready (MMIfaceModemVoice *self,
GAsyncResult *res,
HandleHoldAndAcceptContext *ctx)
{
GError *error = NULL;
GList *l;
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_hold_and_accept_context_free (ctx);
return;
}
for (l = ctx->active_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);
if (ctx->next_call)
mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
mm_gdbus_modem_voice_complete_hold_and_accept (ctx->skeleton, ctx->invocation);
handle_hold_and_accept_context_free (ctx);
}
static void
prepare_hold_and_accept_foreach (MMBaseCall *call,
HandleHoldAndAcceptContext *ctx)
{
switch (mm_base_call_get_state (call)) {
case MM_CALL_STATE_ACTIVE:
ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call));
break;
case MM_CALL_STATE_WAITING:
g_clear_object (&ctx->next_call);
ctx->next_call = g_object_ref (call);
break;
case MM_CALL_STATE_HELD:
if (!ctx->next_call)
ctx->next_call = g_object_ref (call);
break;
default:
break;
}
}
static void
handle_hold_and_accept_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleHoldAndAcceptContext *ctx)
{
MMModemState modem_state = MM_MODEM_STATE_UNKNOWN;
GError *error = NULL;
MMCallList *list = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_hold_and_accept_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
if (modem_state < MM_MODEM_STATE_ENABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot hold and accept: device not yet enabled");
handle_hold_and_accept_context_free (ctx);
return;
}
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept ||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot hold and accept: unsupported");
handle_hold_and_accept_context_free (ctx);
return;
}
g_object_get (self,
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
NULL);
if (!list) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot hold and accept: missing call list");
handle_hold_and_accept_context_free (ctx);
return;
}
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hold_and_accept_foreach, ctx);
g_object_unref (list);
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept (MM_IFACE_MODEM_VOICE (self),
(GAsyncReadyCallback)hold_and_accept_ready,
ctx);
}
static gboolean
handle_hold_and_accept (MmGdbusModemVoice *skeleton,
GDBusMethodInvocation *invocation,
MMIfaceModemVoice *self)
{
HandleHoldAndAcceptContext *ctx;
ctx = g_slice_new0 (HandleHoldAndAcceptContext);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_VOICE,
(GAsyncReadyCallback)handle_hold_and_accept_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModemVoice *skeleton;
GDBusMethodInvocation *invocation;
@@ -1370,6 +1512,10 @@ interface_initialization_step (GTask *task)
"handle-hangup-and-accept",
G_CALLBACK (handle_hangup_and_accept),
self);
g_signal_connect (ctx->skeleton,
"handle-hold-and-accept",
G_CALLBACK (handle_hold_and_accept),
self);
/* Finally, export the new interface */
mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self),
+8
View File
@@ -92,6 +92,14 @@ struct _MMIfaceModemVoice {
MMCallDirection direction,
const gchar *number);
/* Hold and accept */
void (* hold_and_accept) (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* hold_and_accept_finish) (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
/* Hangup and accept */
void (* hangup_and_accept) (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,