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.
1072 lines
29 KiB
C
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;
|
|
}
|