Files
calls/src/calls-manager.c
Evangelos Ribeiro Tzaras db7674d0f9 manager: Add new set of add and remove signals for CuiCalls
This is part of a refactoring which should help separating concerns between
CallsCall and CallsUiCallData better.

To accomplish this we want to stop passing around CallsCall objects and instead
pass CallsUiCallData objects.
In this commit we added new signals instead of changing the existing ones
because that results in a lot of churn. It allows to do the switch per module
allowing smaller individual patches and a more manageable patchset.
2022-02-01 06:11:58 +01:00

1072 lines
29 KiB
C

/*
* Copyright (C) 2020 Purism SPC
*
* This file is part of Calls.
*
* Calls is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calls is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Julian Sparber <julian.sparber@puri.sm>
* Evangelos Ribeiro Tzaras <evangelos.tzaras@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsManager"
#include "config.h"
#include "calls-account.h"
#include "calls-application.h"
#include "calls-contacts-provider.h"
#include "calls-manager.h"
#include "calls-message-source.h"
#include "calls-provider.h"
#include "calls-settings.h"
#include "calls-ui-call-data.h"
#include "calls-ussd.h"
#include "enum-types.h"
#include <glib/gi18n.h>
#include <libpeas/peas.h>
static const char * const protocols[] = {
"tel",
"sip",
"sips"
};
struct _CallsManager
{
GObject parent_instance;
GHashTable *providers;
GListStore *origins;
/* origins_by_protocol maps protocol names to GListStore's of suitable origins */
GHashTable *origins_by_protocol;
/* dial_actions_by_protocol maps protocol names to GSimpleActions */
GHashTable *dial_actions_by_protocol;
/* map CallsCall to CallsUiCallData */
GHashTable *calls;
CallsContactsProvider *contacts_provider;
CallsManagerFlags state_flags;
CallsSettings *settings;
};
static void
calls_manager_message_source_interface_init (CallsMessageSourceInterface *iface)
{
}
G_DEFINE_TYPE_WITH_CODE (CallsManager, calls_manager, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_manager_message_source_interface_init))
enum {
PROP_0,
PROP_STATE_FLAGS,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
enum {
SIGNAL_CALL_ADD,
SIGNAL_CALL_REMOVE,
UI_CALL_ADDDED, /* we're phasing out "call-added" in favour of "ui-call-added" */
UI_CALL_REMOVED,
USSD_ADDED,
USSD_CANCELLED,
USSD_STATE_CHANGED,
PROVIDERS_CHANGED,
SIGNAL_LAST_SIGNAL,
};
static guint signals [SIGNAL_LAST_SIGNAL];
static void
set_state_flags (CallsManager *self, CallsManagerFlags state_flags)
{
if (self->state_flags == state_flags)
return;
self->state_flags = state_flags;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_STATE_FLAGS]);
}
static void
update_state_flags (CallsManager *self)
{
GHashTableIter iter;
gpointer value;
CallsManagerFlags state_flags = CALLS_MANAGER_FLAGS_UNKNOWN;
g_assert (CALLS_IS_MANAGER (self));
g_hash_table_iter_init (&iter, self->providers);
while (g_hash_table_iter_next (&iter, NULL, &value)) {
CallsProvider *provider = CALLS_PROVIDER (value);
if (calls_provider_is_modem (provider)) {
state_flags |= CALLS_MANAGER_FLAGS_HAS_CELLULAR_PROVIDER;
if (calls_provider_is_operational (provider))
state_flags |= CALLS_MANAGER_FLAGS_HAS_CELLULAR_MODEM;
} else {
state_flags |= CALLS_MANAGER_FLAGS_HAS_VOIP_PROVIDER;
if (calls_provider_is_operational (provider))
state_flags |= CALLS_MANAGER_FLAGS_HAS_VOIP_ACCOUNT;
}
}
set_state_flags (self, state_flags);
}
static CallsOrigin *
lookup_origin_by_id (CallsManager *self,
const char *origin_id)
{
return NULL;
}
static void
on_dial_protocol_activated (GSimpleAction *action,
GVariant *parameter,
CallsManager *self)
{
GApplication *application;
CallsOrigin *origin;
g_autofree char *target = NULL;
g_autofree char *origin_id = NULL;
g_variant_get (parameter, "(ss)", &target, &origin_id);
origin = lookup_origin_by_id (self, origin_id);
if (origin) {
calls_origin_dial (origin, target);
return;
}
if (origin_id && *origin_id)
g_debug ("Origin ID '%s' given, but was not found for call to '%s'",
origin_id, target);
/* fall back to the default action if we could not determine origin to place call from */
application = g_application_get_default ();
if (!application) {
g_warning ("Could not get default application, cannot activate action '%s'",
g_action_get_name (G_ACTION (action)));
return;
}
g_action_group_activate_action (G_ACTION_GROUP (application),
"dial",
g_variant_new_string (target));
}
static void
update_protocol_dial_actions (CallsManager *self)
{
g_assert (CALLS_IS_MANAGER (self));
for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) {
GSimpleAction *action = g_hash_table_lookup (self->dial_actions_by_protocol,
protocols[i]);
GListModel *protocol_origin = g_hash_table_lookup (self->origins_by_protocol,
protocols[i]);
/* TODO take into account if origin is active: modem registered or VoIP account online */
gboolean action_enabled = g_list_model_get_n_items (protocol_origin) > 0;
g_simple_action_set_enabled (action, action_enabled);
}
}
static void
on_message (CallsMessageSource *source,
const char *message,
GtkMessageType message_type,
CallsManager *self)
{
g_autofree char *notification = NULL;
g_assert (CALLS_IS_MESSAGE_SOURCE (source));
g_assert (CALLS_IS_MANAGER (self));
/* Prefix the message with the name of the source, if known */
if (CALLS_IS_ACCOUNT (source)) {
notification = g_strdup_printf ("%s: %s",
calls_account_get_address (CALLS_ACCOUNT (source)),
message);
}
calls_message_source_emit_message (CALLS_MESSAGE_SOURCE (self),
notification ? : message,
message_type);
}
static void
add_call (CallsManager *self, CallsCall *call, CallsOrigin *origin)
{
CallsUiCallData *call_data;
g_return_if_fail (CALLS_IS_MANAGER (self));
g_return_if_fail (CALLS_IS_ORIGIN (origin));
g_return_if_fail (CALLS_IS_CALL (call));
call_data = calls_ui_call_data_new (call);
g_hash_table_insert (self->calls, call, call_data);
/* TODO get rid of SIGNAL_CALL_ADD signal */
g_signal_emit (self, signals[SIGNAL_CALL_ADD], 0, call, origin);
g_signal_emit (self, signals[UI_CALL_ADDDED], 0, call_data);
}
static void
remove_call (CallsManager *self, CallsCall *call, gchar *reason, CallsOrigin *origin)
{
CallsUiCallData *call_data;
g_return_if_fail (CALLS_IS_MANAGER (self));
g_return_if_fail (CALLS_IS_ORIGIN (origin));
g_return_if_fail (CALLS_IS_CALL (call));
call_data = g_hash_table_lookup (self->calls, call);
if (!call_data) {
g_warning ("Could not remove call %s from hash table", calls_call_get_id (call));
} else {
g_signal_emit (self, signals[UI_CALL_REMOVED], 0, call_data);
g_hash_table_remove (self->calls, call);
}
/* TODO get rid of SIGNAL_CALL_REMOVE signal */
/* We ignore the reason for now, because it doesn't give any usefull information */
g_signal_emit (self, signals[SIGNAL_CALL_REMOVE], 0, call, origin);
}
static void
ussd_added_cb (CallsManager *self,
char *response,
CallsUssd *ussd)
{
g_assert (CALLS_IS_MANAGER (self));
g_assert (CALLS_IS_USSD (ussd));
g_signal_emit (self, signals[USSD_ADDED], 0, ussd, response);
}
static void
ussd_cancelled_cb (CallsManager *self,
CallsUssd *ussd,
char *response)
{
g_assert (CALLS_IS_MANAGER (self));
g_assert (CALLS_IS_USSD (ussd));
g_signal_emit (self, signals[USSD_CANCELLED], 0, ussd);
}
static void
ussd_state_changed_cb (CallsManager *self,
CallsUssd *ussd)
{
g_assert (CALLS_IS_MANAGER (self));
g_assert (CALLS_IS_USSD (ussd));
g_signal_emit (self, signals[USSD_STATE_CHANGED], 0, ussd);
}
static void
update_country_code_cb (CallsOrigin *origin,
GParamSpec *pspec,
CallsManager *self)
{
g_autofree char *country_code = NULL;
g_assert (CALLS_IS_MANAGER (self));
g_object_get (G_OBJECT (origin), "country-code", &country_code, NULL);
if (!country_code || !*country_code)
return;
calls_settings_set_country_code (self->settings, country_code);
}
static void
add_origin (CallsManager *self, CallsOrigin *origin)
{
g_autofree const char *name = NULL;
g_assert (CALLS_IS_MANAGER (self));
g_assert (CALLS_IS_ORIGIN (origin));
name = calls_origin_get_name (origin);
g_debug ("Adding origin %s (%p)", name, origin);
g_list_store_append (self->origins, origin);
g_signal_connect (origin,
"message",
G_CALLBACK (on_message),
self);
g_signal_connect_object (origin,
"notify::country-code",
G_CALLBACK (update_country_code_cb),
self,
G_CONNECT_AFTER);
g_signal_connect_swapped (origin, "call-added", G_CALLBACK (add_call), self);
g_signal_connect_swapped (origin, "call-removed", G_CALLBACK (remove_call), self);
if (CALLS_IS_USSD (origin))
{
g_signal_connect_swapped (origin, "ussd-added", G_CALLBACK (ussd_added_cb), self);
g_signal_connect_swapped (origin, "ussd-cancelled", G_CALLBACK (ussd_cancelled_cb), self);
g_signal_connect_swapped (origin, "ussd-state-changed", G_CALLBACK (ussd_state_changed_cb), self);
}
calls_origin_foreach_call (origin, (CallsOriginForeachCallFunc) add_call, self);
}
static void
remove_call_cb (gpointer self, CallsCall *call, CallsOrigin *origin)
{
remove_call (self, call, NULL, origin);
}
static void
remove_origin (CallsManager *self, CallsOrigin *origin)
{
g_autofree const char *name = NULL;
guint position;
g_assert (CALLS_IS_MANAGER (self));
g_assert (CALLS_IS_ORIGIN (origin));
name = calls_origin_get_name (origin);
g_debug ("Removing origin %s (%p)", name, origin);
g_signal_handlers_disconnect_by_data (origin, self);
calls_origin_foreach_call (origin, remove_call_cb, self);
if (!g_list_store_find (self->origins, origin, &position))
g_warning ("Origin %p not found in list store while trying to remove it",
origin);
else
g_list_store_remove (self->origins, position);
}
/* rebuild_origins_by_protocols() when any origins were added or removed */
static void
rebuild_origins_by_protocols (CallsManager *self)
{
GHashTableIter iter;
gpointer value;
guint n_origins;
g_assert (CALLS_IS_MANAGER (self));
/* Remove everything */
g_hash_table_iter_init (&iter, self->origins_by_protocol);
while (g_hash_table_iter_next (&iter, NULL, &value)) {
GListStore *store = G_LIST_STORE (value);
g_list_store_remove_all (store);
}
/* Iterate over all origins and check which protocols they support */
n_origins = g_list_model_get_n_items (G_LIST_MODEL (self->origins));
for (guint i = 0; i < n_origins; i++) {
g_autoptr (CallsOrigin) origin =
g_list_model_get_item (G_LIST_MODEL (self->origins), i);
for (guint j = 0; j < G_N_ELEMENTS (protocols); j++) {
GListStore *store =
G_LIST_STORE (g_hash_table_lookup (self->origins_by_protocol, protocols[j]));
g_assert (store);
if (calls_origin_supports_protocol (origin, protocols[j]))
g_list_store_append (store, origin);
}
}
update_protocol_dial_actions (self);
}
static void
remove_provider (CallsManager *self,
const char *name)
{
GListModel *origins;
guint n_items;
g_autoptr (CallsProvider) provider = NULL;
g_assert (CALLS_IS_MANAGER (self));
g_assert (name);
provider = g_hash_table_lookup (self->providers, name);
if (provider) {
/* Hold a ref since g_hash_table_remove () might drop the last one */
g_object_ref (provider);
} else {
g_warning ("Trying to remove provider %s which has not been found", name);
return;
}
g_debug ("Remove provider: %s", name);
g_signal_handlers_disconnect_by_data (provider, self);
origins = calls_provider_get_origins (provider);
g_signal_handlers_disconnect_by_data (origins, self);
n_items = g_list_model_get_n_items (origins);
for (guint i = 0; i < n_items; i++) {
g_autoptr(CallsOrigin) origin = NULL;
origin = g_list_model_get_item (origins, i);
remove_origin (self, origin);
}
g_hash_table_remove (self->providers, name);
calls_provider_unload_plugin (name);
rebuild_origins_by_protocols (self);
update_state_flags (self);
g_signal_emit (self, signals[PROVIDERS_CHANGED], 0);
}
static gboolean
origin_found_in_any_provider (CallsManager *self,
CallsOrigin *origin)
{
GHashTableIter iter;
gpointer value;
g_return_val_if_fail (CALLS_IS_MANAGER (self), FALSE);
g_return_val_if_fail (CALLS_IS_ORIGIN (origin), FALSE);
g_hash_table_iter_init (&iter, self->providers);
while (g_hash_table_iter_next (&iter, NULL, &value)) {
guint position;
CallsProvider *provider = CALLS_PROVIDER (value);
GListModel *origins = calls_provider_get_origins (provider);
if (origins && calls_find_in_store (origins,
origin,
&position))
return TRUE;
}
return FALSE;
}
static void
origin_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
CallsManager *self)
{
guint i;
CallsOrigin *origin;
guint purged = 0;
guint total_origins;
g_assert (CALLS_IS_MANAGER (self));
total_origins = g_list_model_get_n_items (G_LIST_MODEL (self->origins));
g_debug ("origins changed: pos=%d rem=%d added=%d total=%d",
position, removed, added, g_list_model_get_n_items (model));
/* Check stale/removed origins: We need to look up */
if (removed == 0)
goto skip_remove;
for (i = 0; i < total_origins - purged; i++) {
origin = g_list_model_get_item (G_LIST_MODEL (self->origins), i - purged);
if (!origin_found_in_any_provider (self, origin)) {
remove_origin (self, origin);
purged++;
}
}
/** The number of purged entries from self->origins must be equal to removed
* origins from the providers list
*/
if (purged != removed) {
g_warning ("Managed origins are not in sync anymore!");
}
skip_remove:
for (i = 0; i < added; i++) {
g_debug ("before adding: %d",
g_list_model_get_n_items (G_LIST_MODEL (self->origins)));
origin = g_list_model_get_item (model, position + i);
add_origin (self, origin); // add to list store
g_object_unref (origin);
g_debug ("after adding: %d",
g_list_model_get_n_items (G_LIST_MODEL (self->origins)));
}
rebuild_origins_by_protocols (self);
update_state_flags (self);
}
static void
add_provider (CallsManager *self, const gchar *name)
{
GListModel *origins;
CallsProvider *provider;
guint n_items;
g_assert (CALLS_IS_MANAGER (self));
g_assert (name);
if (g_hash_table_lookup (self->providers, name))
return;
provider = calls_provider_load_plugin (name);
if (provider == NULL) {
g_warning ("Could not load a plugin with name `%s'", name);
return;
}
g_hash_table_insert (self->providers, g_strdup (name), provider);
origins = calls_provider_get_origins (provider);
g_signal_connect_object (origins, "items-changed",
G_CALLBACK (origin_items_changed_cb), self,
G_CONNECT_AFTER);
n_items = g_list_model_get_n_items (origins);
origin_items_changed_cb (origins, 0, 0, n_items, self);
g_signal_emit (self, signals[PROVIDERS_CHANGED], 0);
}
static void
calls_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CallsManager *self = CALLS_MANAGER (object);
switch (property_id) {
case PROP_STATE_FLAGS:
g_value_set_flags (value, calls_manager_get_state_flags (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
calls_manager_finalize (GObject *object)
{
CallsManager *self = CALLS_MANAGER (object);
g_clear_object (&self->origins);
g_clear_object (&self->contacts_provider);
g_clear_object (&self->settings);
g_clear_pointer (&self->providers, g_hash_table_unref);
g_clear_pointer (&self->origins_by_protocol, g_hash_table_unref);
g_clear_pointer (&self->dial_actions_by_protocol, g_hash_table_unref);
G_OBJECT_CLASS (calls_manager_parent_class)->finalize (object);
}
static void
calls_manager_class_init (CallsManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = calls_manager_get_property;
object_class->finalize = calls_manager_finalize;
signals[SIGNAL_CALL_ADD] =
g_signal_new ("call-add",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
2,
CALLS_TYPE_CALL,
CALLS_TYPE_ORIGIN);
signals[SIGNAL_CALL_REMOVE] =
g_signal_new ("call-remove",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
2,
CALLS_TYPE_CALL,
CALLS_TYPE_ORIGIN);
signals[UI_CALL_ADDDED] =
g_signal_new ("ui-call-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
CALLS_TYPE_UI_CALL_DATA);
signals[UI_CALL_REMOVED] =
g_signal_new ("ui-call-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
CALLS_TYPE_UI_CALL_DATA);
signals[USSD_ADDED] =
g_signal_new ("ussd-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
2,
CALLS_TYPE_USSD,
G_TYPE_STRING);
signals[USSD_CANCELLED] =
g_signal_new ("ussd-cancelled",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
CALLS_TYPE_USSD);
signals[USSD_STATE_CHANGED] =
g_signal_new ("ussd-state-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
CALLS_TYPE_USSD);
signals[PROVIDERS_CHANGED] =
g_signal_new ("providers-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
0);
props[PROP_STATE_FLAGS] =
g_param_spec_flags ("state",
"state",
"The state of the Manager",
CALLS_TYPE_MANAGER_FLAGS,
CALLS_MANAGER_FLAGS_UNKNOWN,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
calls_manager_init (CallsManager *self)
{
g_autoptr (GVariantType) variant_type = NULL;
GApplication *application;
PeasEngine *peas;
const gchar *dir;
self->state_flags = CALLS_MANAGER_FLAGS_UNKNOWN;
self->providers = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
self->origins_by_protocol = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) {
GListStore *origin_store = g_list_store_new (calls_origin_get_type ());
g_hash_table_insert (self->origins_by_protocol,
g_strdup (protocols[i]),
origin_store);
}
self->dial_actions_by_protocol = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
application = g_application_get_default ();
variant_type = g_variant_type_new ("(ss)");
for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) {
g_autofree char *action_name = g_strdup_printf ("dial-%s", protocols[i]);
GSimpleAction *action = g_simple_action_new (action_name, variant_type);
g_signal_connect (action,
"activate",
G_CALLBACK (on_dial_protocol_activated),
self);
g_hash_table_insert (self->dial_actions_by_protocol,
g_strdup (protocols[i]),
g_object_ref (action));
/* Enable action if there are suitable origins */
g_simple_action_set_enabled (action, FALSE);
/* application can be NULL when running tests */
if (application)
g_action_map_add_action (G_ACTION_MAP (application), G_ACTION (action));
}
self->origins = g_list_store_new (calls_origin_get_type ());
/* This hash table only owns the value, not the key */
self->calls = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
self->settings = calls_settings_new ();
// Load the contacts provider
self->contacts_provider = calls_contacts_provider_new (self->settings);
peas = peas_engine_get_default ();
dir = g_getenv ("CALLS_PLUGIN_DIR");
if (dir && dir[0] != '\0') {
/** Add the directory to the search path. prepend_search_path() does not work
* as expected. see https://gitlab.gnome.org/GNOME/libpeas/-/issues/19
*/
g_debug ("Adding %s to plugin search path", dir);
peas_engine_add_search_path (peas, dir, NULL);
}
peas_engine_add_search_path (peas, PLUGIN_LIBDIR, NULL);
g_debug ("Scanning for plugins in `%s'", PLUGIN_LIBDIR);
}
CallsManager *
calls_manager_new (void)
{
return g_object_new (CALLS_TYPE_MANAGER, NULL);
}
CallsManager *
calls_manager_get_default (void)
{
static CallsManager *instance;
if (instance == NULL) {
instance = calls_manager_new ();
g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
}
return instance;
}
CallsContactsProvider *
calls_manager_get_contacts_provider (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
return self->contacts_provider;
}
void
calls_manager_add_provider (CallsManager *self,
const char *name)
{
g_return_if_fail (CALLS_IS_MANAGER (self));
g_return_if_fail (name);
add_provider (self, name);
}
void
calls_manager_remove_provider (CallsManager *self,
const char *name)
{
g_return_if_fail (CALLS_IS_MANAGER (self));
g_return_if_fail (name);
remove_provider (self, name);
}
gboolean
calls_manager_has_provider (CallsManager *self,
const char *name)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), FALSE);
g_return_val_if_fail (name, FALSE);
return !!g_hash_table_lookup (self->providers, name);
}
gboolean
calls_manager_is_modem_provider (CallsManager *self,
const char *name)
{
CallsProvider *provider;
g_return_val_if_fail (CALLS_IS_MANAGER (self), FALSE);
g_return_val_if_fail (name, FALSE);
provider = g_hash_table_lookup (self->providers, name);
g_return_val_if_fail (provider, FALSE);
return calls_provider_is_modem (provider);
}
CallsManagerFlags
calls_manager_get_state_flags (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), CALLS_MANAGER_FLAGS_UNKNOWN);
return self->state_flags;
}
GListModel *
calls_manager_get_origins (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
return G_LIST_MODEL (self->origins);
}
GList *
calls_manager_get_calls (CallsManager *self)
{
GListModel *origins = NULL;
g_autoptr (GList) calls = NULL;
guint n_items = 0;
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
origins = calls_manager_get_origins (self);
if (origins)
n_items = g_list_model_get_n_items (origins);
for (guint i = 0; i < n_items; i++)
{
g_autoptr (CallsOrigin) origin = NULL;
origin = g_list_model_get_item (origins, i);
calls = g_list_concat (calls, calls_origin_get_calls (origin));
}
return g_steal_pointer (&calls);
}
/**
* calls_manager_hang_up_all_calls:
* @self: a #CallsManager
*
* Hangs up on every call known to @self.
*/
void
calls_manager_hang_up_all_calls (CallsManager *self)
{
g_autoptr (GList) calls = NULL;
GList *node;
CallsCall *call;
g_return_if_fail (CALLS_IS_MANAGER (self));
calls = calls_manager_get_calls (self);
for (node = calls; node; node = node->next)
{
call = node->data;
g_debug ("Hanging up on call %s", calls_call_get_name (call));
calls_call_hang_up (call);
}
g_debug ("Hanged up on all calls");
}
/**
* calls_manager_has_active_call
* @self: a #CallsManager
*
* Checks if @self has any active call
*
* Returns: %TRUE if there are active calls, %FALSE otherwise
*/
gboolean
calls_manager_has_active_call (CallsManager *self)
{
g_autoptr (GList) calls = NULL;
GList *node;
CallsCall *call;
g_return_val_if_fail (CALLS_IS_MANAGER (self), FALSE);
calls = calls_manager_get_calls (self);
for (node = calls; node; node = node->next)
{
call = node->data;
if (calls_call_get_state (call) != CALLS_CALL_STATE_DISCONNECTED)
return TRUE;
}
return FALSE;
}
/**
* calls_manager_get_suitable_origins:
* @self: The #CallsManager
* @target: The target number/address
*
* Returns (transfer none): A #GListModel of suitable origins as long
* as the protocol to be used for @target is supported, %NULL otherwise
*/
GListModel *
calls_manager_get_suitable_origins (CallsManager *self,
const char *target)
{
const char *protocol;
GListModel *model;
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
g_return_val_if_fail (target, NULL);
protocol = get_protocol_from_address_with_fallback (target);
model = g_hash_table_lookup (self->origins_by_protocol, protocol);
if (model && G_IS_LIST_MODEL (model))
return model;
return NULL;
}
/**
* calls_manager_has_any_provider:
* @self: The #CallsManager
*
* Returns: %TRUE if any provider is loaded, %FALSE otherwise
*/
gboolean
calls_manager_has_any_provider (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), FALSE);
return !!g_hash_table_size (self->providers);
}
/**
* calls_manager_get_provider_names:
* @self: The #CallsManager
* @length: (optional) (out): the length of the returned array
*
* Retrieves the names of all providers loaded by @self, as an array.
*
* You should free the return value with g_free().
*
* Returns: (array length=length) (transfer container): a
* %NULL-terminated array containing the names of providers.
*/
const char **
calls_manager_get_provider_names (CallsManager *self,
guint *length)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
return (const char **) g_hash_table_get_keys_as_array (self->providers, length);
}
/**
* calls_manager_get_providers:
* @self: A #CallsManager
*
* Get the currently loaded providers
*
* Returns: (transfer container): A #GList of #CallsProvider.
* Use g_list_free() when done using the list.
*/
GList *
calls_manager_get_providers (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
return g_hash_table_get_values (self->providers);
}
CallsSettings *
calls_manager_get_settings (CallsManager *self)
{
g_return_val_if_fail (CALLS_IS_MANAGER (self), NULL);
return self->settings;
}