Turn providers into plugins courtesy of libpeas

This is an initial, static implementation of plugins.  The
CallsApplication has a plugin name which can be changed with a new
--provider command line option.  This plugin name is used to
instantiate the appropriate plugin when the application is activated.
From then on, the plugin cannot change.

In future, we can expand this support to include loading multiple
plugins at once, configurable through some UI.  This will have
far-reaching implications though, and complicate things like
enumerating the provider hierarchy.  There is also no practical
benefit right now; the mm and ofono plugins can't be used at the same
time because ModemManager and oFono don't play nice together, and the
whole raison d'être of the dummy plugin is undermined if you can make
use of one of the others.  So for now, we just implement one static
plugin.
This commit is contained in:
Bob Ham 2018-11-23 14:34:41 +00:00
parent b6cd5a3fe8
commit 460c0c6c3d
39 changed files with 672 additions and 181 deletions

1
debian/control vendored
View File

@ -9,6 +9,7 @@ Build-Depends:
modemmanager-dev,
libmm-glib-dev,
libgsound-dev,
libpeas-dev,
meson,
pkg-config,
# to run the tests

View File

@ -1,19 +0,0 @@
gnome = import('gnome')
dbus_interfaces = ['manager', 'modem', 'call']
gdbus_src = []
foreach iface: dbus_interfaces
gdbus_src += gnome.gdbus_codegen('gdbo-' + iface,
iface + '.xml',
interface_prefix: 'org.ofono.',
namespace: 'GDBO')
endforeach
deps = [ dependency('gio-2.0'),
dependency('gio-unix-2.0'),
]
gdbofono_lib = static_library('gdbofono',
gdbus_src,
include_directories : include_directories('..'),
dependencies : deps )

View File

@ -19,11 +19,17 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
project('call', 'c',
version: '0.0.0',
license: 'GPLv3+',
meson_version: '>= 0.42.0',
default_options: [ 'warning_level=1', 'buildtype=debugoptimized', 'c_std=gnu11' ],
project(
'calls',
'c',
version: '0.0.0',
license: 'GPLv3+',
meson_version: '>= 0.46.0',
default_options: [
'warning_level=1',
'buildtype=debugoptimized',
'c_std=gnu11'
]
)
calls_id = 'sm.puri.Calls'
@ -31,9 +37,26 @@ calls_homepage = 'https://source.puri.sm/Librem5/calls'
calls_name = meson.project_name()
calls_version = meson.project_version()
subdir('libgdbofono')
top_include = include_directories('.')
prefix = get_option('prefix')
libdir = get_option('libdir')
localedir = get_option('localedir')
full_localedir = join_paths(prefix, localedir)
full_calls_plugin_libdir = join_paths(prefix, libdir, calls_name, 'plugins')
config_data = configuration_data()
config_data.set_quoted('APP_ID', calls_id)
config_data.set_quoted('GETTEXT_PACKAGE', calls_name)
config_data.set_quoted('LOCALEDIR', full_localedir)
config_data.set_quoted('PLUGIN_LIBDIR', full_calls_plugin_libdir)
config_data.set_quoted('PACKAGE_URL', calls_homepage)
config_data.set_quoted('PACKAGE_VERSION', calls_version)
config_data.set('PACKAGE_URL_RAW', calls_homepage)
subdir('po')
subdir('src')
subdir('plugins')
subdir('doc')
subdir('data')
subdir('tests')

View File

@ -27,6 +27,9 @@
#include "calls-provider.h"
#include "calls-dummy-origin.h"
#include <libpeas/peas.h>
struct _CallsDummyProvider
{
GObject parent_instance;
@ -37,12 +40,26 @@ struct _CallsDummyProvider
static void calls_dummy_provider_message_source_interface_init (CallsProviderInterface *iface);
static void calls_dummy_provider_provider_interface_init (CallsProviderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsDummyProvider, calls_dummy_provider, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_dummy_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_PROVIDER,
calls_dummy_provider_provider_interface_init))
#ifdef FOR_TESTING
G_DEFINE_TYPE_WITH_CODE
(CallsDummyProvider, calls_dummy_provider, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_dummy_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_PROVIDER,
calls_dummy_provider_provider_interface_init))
#else
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(CallsDummyProvider, calls_dummy_provider, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_MESSAGE_SOURCE,
calls_dummy_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_PROVIDER,
calls_dummy_provider_provider_interface_init))
#endif /* FOR_TESTING */
enum {
PROP_0,
@ -65,10 +82,15 @@ get_origins (CallsProvider *iface)
}
CallsDummyProvider *
calls_dummy_provider_new ()
static void
constructed (GObject *object)
{
return g_object_new (CALLS_TYPE_DUMMY_PROVIDER, NULL);
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
CallsDummyProvider *self = CALLS_DUMMY_PROVIDER (object);
calls_dummy_provider_add_origin (self, "Dummy origin");
parent_class->constructed (object);
}
@ -108,8 +130,9 @@ calls_dummy_provider_class_init (CallsDummyProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = dispose;
object_class->constructed = constructed;
object_class->get_property = get_property;
object_class->dispose = dispose;
g_object_class_override_property (object_class, PROP_STATUS, "status");
}
@ -142,3 +165,31 @@ calls_dummy_provider_add_origin (CallsDummyProvider *self,
self->origins = g_list_append (self->origins,
calls_dummy_origin_new (name));
}
CallsDummyProvider *
calls_dummy_provider_new ()
{
return g_object_new (CALLS_TYPE_DUMMY_PROVIDER, NULL);
}
#ifndef FOR_TESTING
static void
calls_dummy_provider_class_finalize (CallsDummyProviderClass *klass)
{
}
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
calls_dummy_provider_register_type (G_TYPE_MODULE (module));
peas_object_module_register_extension_type (module,
CALLS_TYPE_PROVIDER,
CALLS_TYPE_DUMMY_PROVIDER);
}
#endif /* FOR_TESTING */

View File

@ -0,0 +1,7 @@
[Plugin]
Module=dummy
Name=Dummy
Description=Dummy calls provider
Authors=Bob Ham <rah@settrans.net>
Copyright=Copyright (C) 2018 Purism SPC
Website=@PACKAGE_URL_RAW@

56
plugins/dummy/meson.build Normal file
View File

@ -0,0 +1,56 @@
#
# Copyright (C) 2018 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: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
dummy_include = include_directories('.')
dummy_install_dir = join_paths(full_calls_plugin_libdir, 'dummy')
dummy_plugin = configure_file(
input: 'dummy.plugin.in',
output: 'dummy.plugin',
configuration: config_data,
install_dir: dummy_install_dir
)
dummy_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('libpeas-1.0'),
]
dummy_sources = files(
[
'calls-dummy-call.c', 'calls-dummy-call.h',
'calls-dummy-origin.c', 'calls-dummy-origin.h',
'calls-dummy-provider.c', 'calls-dummy-provider.h'
]
)
shared_module(
'dummy',
dummy_sources,
dependencies: dummy_deps,
include_directories: src_include,
install: true,
install_dir: dummy_install_dir
)

3
plugins/meson.build Normal file
View File

@ -0,0 +1,3 @@
subdir('mm')
subdir('dummy')
subdir('ofono')

View File

@ -29,6 +29,7 @@
#include "calls-origin.h"
#include <libmm-glib.h>
#include <libpeas/peas.h>
#include <glib/gi18n.h>
struct _CallsMMProvider
@ -48,11 +49,13 @@ struct _CallsMMProvider
static void calls_mm_provider_message_source_interface_init (CallsProviderInterface *iface);
static void calls_mm_provider_provider_interface_init (CallsProviderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsMMProvider, calls_mm_provider, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_PROVIDER,
calls_mm_provider_provider_interface_init))
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(CallsMMProvider, calls_mm_provider, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_PROVIDER,
calls_mm_provider_provider_interface_init))
enum {
PROP_0,
@ -436,6 +439,11 @@ calls_mm_provider_class_init (CallsMMProviderClass *klass)
}
static void
calls_mm_provider_class_finalize (CallsMMProviderClass *klass)
{
}
static void
calls_mm_provider_message_source_interface_init (CallsProviderInterface *iface)
{
@ -459,8 +467,12 @@ calls_mm_provider_init (CallsMMProvider *self)
}
CallsMMProvider *
calls_mm_provider_new ()
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
return g_object_new (CALLS_TYPE_MM_PROVIDER, NULL);
calls_mm_provider_register_type (G_TYPE_MODULE (module));
peas_object_module_register_extension_type (module,
CALLS_TYPE_PROVIDER,
CALLS_TYPE_MM_PROVIDER);
}

View File

@ -34,8 +34,6 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (CallsMMProvider, calls_mm_provider, CALLS, MM_PROVIDER, GObject);
CallsMMProvider *calls_mm_provider_new ();
G_END_DECLS
#endif /* CALLS_MM_PROVIDER_H__ */

56
plugins/mm/meson.build Normal file
View File

@ -0,0 +1,56 @@
#
# Copyright (C) 2018 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: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
mm_install_dir = join_paths(full_calls_plugin_libdir, 'mm')
mm_plugin = configure_file(
input: 'mm.plugin.in',
output: 'mm.plugin',
configuration: config_data,
install_dir: mm_install_dir
)
mm_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('ModemManager'),
dependency('mm-glib'),
dependency('libpeas-1.0'),
]
mm_sources = files(
[
'calls-mm-call.c', 'calls-mm-call.h',
'calls-mm-origin.c', 'calls-mm-origin.h',
'calls-mm-provider.c', 'calls-mm-provider.h'
]
)
shared_module(
'mm',
mm_sources,
dependencies: mm_deps,
include_directories: src_include,
install: true,
install_dir: mm_install_dir
)

7
plugins/mm/mm.plugin.in Normal file
View File

@ -0,0 +1,7 @@
[Plugin]
Module=mm
Name=ModemManager
Description=ModemManager calls provider
Authors=Bob Ham <rah@settrans.net>
Copyright=Copyright (C) 2018 Purism SPC
Website=@PACKAGE_URL_RAW@

View File

@ -32,6 +32,8 @@
#include <libgdbofono/gdbo-modem.h>
#include <glib/gi18n.h>
#include <libpeas/peas.h>
struct _CallsOfonoProvider
{
@ -47,21 +49,17 @@ struct _CallsOfonoProvider
GHashTable *origins;
};
static void calls_ofono_provider_message_source_interface_init (CallsProviderInterface *iface);
static void calls_ofono_provider_provider_interface_init (CallsProviderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsOfonoProvider, calls_ofono_provider, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_ofono_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_PROVIDER,
calls_ofono_provider_provider_interface_init))
enum {
PROP_0,
PROP_CONNECTION,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(CallsOfonoProvider, calls_ofono_provider, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_MESSAGE_SOURCE,
calls_ofono_provider_message_source_interface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_PROVIDER,
calls_ofono_provider_provider_interface_init))
static const gchar *
@ -70,6 +68,7 @@ get_name (CallsProvider *iface)
return "oFono";
}
static void
add_origin_to_list (const gchar *path,
CallsOfonoOrigin *origin,
@ -78,6 +77,7 @@ add_origin_to_list (const gchar *path,
*list = g_list_prepend (*list, origin);
}
static GList *
get_origins (CallsProvider *iface)
{
@ -90,36 +90,6 @@ get_origins (CallsProvider *iface)
return g_list_reverse (list);
}
CallsOfonoProvider *
calls_ofono_provider_new (GDBusConnection *connection)
{
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
return g_object_new (CALLS_TYPE_OFONO_PROVIDER,
"connection", connection,
NULL);
}
static void
set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
switch (property_id) {
case PROP_CONNECTION:
g_set_object (&self->connection,
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
add_origin (CallsOfonoProvider *self,
@ -137,6 +107,7 @@ add_origin (CallsOfonoProvider *self,
"origin-added", origin);
}
static void
remove_origin (CallsOfonoProvider *self,
const gchar *path,
@ -151,6 +122,7 @@ remove_origin (CallsOfonoProvider *self,
g_object_unref (origin);
}
static gboolean
object_array_includes (GVariantIter *iter,
const gchar *needle)
@ -170,6 +142,7 @@ object_array_includes (GVariantIter *iter,
return found;
}
static void
modem_check_ifaces (CallsOfonoProvider *self,
GDBOModem *modem,
@ -199,6 +172,7 @@ modem_check_ifaces (CallsOfonoProvider *self,
}
}
static void
modem_property_changed_cb (GDBOModem *modem,
const gchar *name,
@ -220,6 +194,7 @@ modem_property_changed_cb (GDBOModem *modem,
modem_check_ifaces (self, modem, modem_name, value);
}
struct CallsModemProxyNewData
{
CallsOfonoProvider *self;
@ -227,6 +202,7 @@ struct CallsModemProxyNewData
GVariant *ifaces;
};
static void
modem_proxy_new_cb (GDBusConnection *connection,
GAsyncResult *res,
@ -274,6 +250,7 @@ modem_proxy_new_cb (GDBusConnection *connection,
g_debug ("Modem `%s' added", path);
}
static gchar *
modem_properties_get_name (GVariant *properties)
{
@ -297,6 +274,7 @@ modem_properties_get_name (GVariant *properties)
return NULL;
}
static void
modem_added_cb (GDBOManager *manager,
const gchar *path,
@ -336,6 +314,7 @@ modem_added_cb (GDBOManager *manager,
g_debug ("Modem `%s' addition in progress", path);
}
static void
modem_removed_cb (GDBOManager *manager,
const gchar *path,
@ -405,6 +384,13 @@ constructed (GObject *object)
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
GError *error = NULL;
self->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!self->connection)
{
g_error ("Error creating D-Bus connection: %s",
error->message);
}
self->manager = gdbo_manager_proxy_new_sync
(self->connection,
G_DBUS_PROXY_FLAGS_NONE,
@ -416,7 +402,6 @@ constructed (GObject *object)
{
g_error ("Error creating ModemManager object manager proxy: %s",
error->message);
return;
}
g_signal_connect (self->manager, "modem-added",
@ -440,14 +425,13 @@ dispose (GObject *object)
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
// FIXME
g_clear_object (&self->manager);
g_clear_object (&self->connection);
parent_class->dispose (object);
}
static void
finalize (GObject *object)
{
@ -466,26 +450,24 @@ calls_ofono_provider_class_init (CallsOfonoProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
props[PROP_CONNECTION] =
g_param_spec_object ("connection",
_("Connection"),
_("The D-Bus connection to use for communication with oFono"),
G_TYPE_DBUS_CONNECTION,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
calls_ofono_provider_class_finalize (CallsOfonoProviderClass *klass)
{
}
static void
calls_ofono_provider_message_source_interface_init (CallsProviderInterface *iface)
{
}
static void
calls_ofono_provider_provider_interface_init (CallsProviderInterface *iface)
{
@ -493,6 +475,7 @@ calls_ofono_provider_provider_interface_init (CallsProviderInterface *iface)
iface->get_origins = get_origins;
}
static void
calls_ofono_provider_init (CallsOfonoProvider *self)
{
@ -501,3 +484,14 @@ calls_ofono_provider_init (CallsOfonoProvider *self)
self->origins = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
}
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
calls_ofono_provider_register_type (G_TYPE_MODULE (module));
peas_object_module_register_extension_type (module,
CALLS_TYPE_PROVIDER,
CALLS_TYPE_OFONO_PROVIDER);
}

View File

@ -34,8 +34,6 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (CallsOfonoProvider, calls_ofono_provider, CALLS, OFONO_PROVIDER, GObject);
CallsOfonoProvider *calls_ofono_provider_new (GDBusConnection *connection);
G_END_DECLS
#endif /* CALLS_OFONO_PROVIDER_H__ */

View File

@ -0,0 +1,51 @@
#
# Copyright (C) 2018 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: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
gnome = import('gnome')
dbus_interfaces = ['manager', 'modem', 'call']
gdbofono_src = []
gdbofono_headers = []
foreach iface: dbus_interfaces
src = gnome.gdbus_codegen(
'gdbo-' + iface,
iface + '.xml',
interface_prefix: 'org.ofono.',
namespace: 'GDBO'
)
gdbofono_src += src
gdbofono_headers += src[1]
endforeach
gdbofono_deps = [
dependency('gio-2.0'),
dependency('gio-unix-2.0'),
]
gdbofono_lib = static_library(
'gdbofono',
gdbofono_src,
include_directories: top_include,
dependencies: gdbofono_deps
)

60
plugins/ofono/meson.build Normal file
View File

@ -0,0 +1,60 @@
#
# Copyright (C) 2018 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: Bob Ham <bob.ham@puri.sm>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
subdir('libgdbofono')
ofono_install_dir = join_paths(full_calls_plugin_libdir, 'ofono')
ofono_plugin = configure_file(
input: 'ofono.plugin.in',
output: 'ofono.plugin',
configuration: config_data,
install_dir: ofono_install_dir
)
ofono_deps = [
dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('libpeas-1.0'),
]
ofono_sources = files(
[
'calls-ofono-call.c', 'calls-ofono-call.h',
'calls-ofono-origin.c', 'calls-ofono-origin.h',
'calls-ofono-provider.c', 'calls-ofono-provider.h'
]
)
shared_module(
'ofono',
ofono_sources, gdbofono_headers,
dependencies: ofono_deps,
include_directories: [
src_include,
include_directories('.')
],
link_with: gdbofono_lib,
install: true,
install_dir: ofono_install_dir
)

View File

@ -0,0 +1,7 @@
[Plugin]
Module=ofono
Name=oFono
Description=oFono calls provider
Authors=Bob Ham <rah@settrans.net>
Copyright=Copyright (C) 2018 Purism SPC
Website=@PACKAGE_URL_RAW@

View File

@ -25,23 +25,23 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <glib/gi18n.h>
#define HANDY_USE_UNSTABLE_API
#include <handy.h>
#include "config.h"
#include "calls-new-call-header-bar.h"
#include "calls-history-header-bar.h"
#include "calls-history-box.h"
#include "calls-new-call-box.h"
#include "calls-encryption-indicator.h"
#include "calls-mm-provider.h"
#include "calls-ringer.h"
#include "calls-call-window.h"
#include "calls-main-window.h"
#include "calls-application.h"
#define HANDY_USE_UNSTABLE_API
#include <handy.h>
#include <libpeas/peas.h>
#include <glib/gi18n.h>
/**
* SECTION: calls-application
* @title: CallsApplication
@ -49,10 +49,13 @@
* @include: "calls-application.h"
*/
#define DEFAULT_PROVIDER_PLUGIN "mm"
struct _CallsApplication
{
GtkApplication parent_instance;
GString *provider_name;
CallsProvider *provider;
CallsRinger *ringer;
};
@ -60,6 +63,217 @@ struct _CallsApplication
G_DEFINE_TYPE (CallsApplication, calls_application, GTK_TYPE_APPLICATION)
static gint
handle_local_options (GApplication *application,
GVariantDict *options)
{
gboolean ok;
g_autoptr(GError) error = NULL;
const gchar *name;
g_debug ("Registering application");
ok = g_application_register (application, NULL, &error);
if (!ok)
{
g_error ("Error registering application: %s",
error->message);
}
ok = g_variant_dict_lookup (options, "provider", "&s", &name);
if (ok)
{
g_action_group_activate_action (G_ACTION_GROUP (application),
"set-provider-name",
g_variant_new_string (name));
}
return -1; // Continue processing signal
}
static void
set_provider_name_action (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
CallsApplication *self = CALLS_APPLICATION (user_data);
const gchar *name;
name = g_variant_get_string (parameter, NULL);
g_return_if_fail (name != NULL);
if (self->provider)
{
g_warning ("Cannot set provider name to `%s'"
" because provider is already created",
name);
return;
}
g_string_assign (self->provider_name, name);
g_debug ("Provider name set to `%s'",
self->provider_name->str);
}
static const GActionEntry actions[] =
{
{ "set-provider-name", set_provider_name_action, "s" },
};
static void
startup (GApplication *application)
{
G_APPLICATION_CLASS (calls_application_parent_class)->startup (application);
g_set_prgname (APP_ID);
g_set_application_name (_("Calls"));
g_action_map_add_action_entries (G_ACTION_MAP (application),
actions,
G_N_ELEMENTS (actions),
application);
}
static void
load_provider_plugin (CallsApplication *self)
{
const gchar * const name = self->provider_name->str;
PeasEngine *plugins;
PeasPluginInfo *info;
PeasExtension *extension;
g_assert (self->provider == NULL);
// Add Calls search path and rescan
plugins = peas_engine_get_default ();
peas_engine_add_search_path (plugins, PLUGIN_LIBDIR, PLUGIN_LIBDIR);
g_debug ("Scanning for plugins in `%s'", PLUGIN_LIBDIR);
// Find the plugin
info = peas_engine_get_plugin_info (plugins, name);
if (!info)
{
g_critical ("Could not find plugin `%s'", name);
return;
}
// Possibly load the plugin
if (!peas_plugin_info_is_loaded (info))
{
g_autoptr(GError) error = NULL;
peas_engine_load_plugin (plugins, info);
if (!peas_plugin_info_is_available (info, &error))
{
if (error)
{
g_critical ("Error loading plugin `%s': %s",
name, error->message);
}
else
{
g_critical ("Could not load plugin `%s'", name);
}
return;
}
g_debug ("Loaded plugin `%s'", name);
}
// Check the plugin provides CallsProvider
if (!peas_engine_provides_extension
(plugins, info, CALLS_TYPE_PROVIDER))
{
g_critical ("Plugin `%s' does not have a provider extension",
name);
return;
}
// Get the extension
extension = peas_engine_create_extensionv
(plugins, info, CALLS_TYPE_PROVIDER, 0, NULL);
if (!extension)
{
g_critical ("Could not create provider from plugin `%s'",
name);
return;
}
g_debug ("Created provider from plugin `%s'", name);
self->provider = CALLS_PROVIDER (extension);
}
static void
activate (GApplication *application)
{
GtkApplication *gtk_app;
GtkWindow *window;
g_assert (GTK_IS_APPLICATION (application));
gtk_app = GTK_APPLICATION (application);
window = gtk_application_get_active_window (gtk_app);
if (window == NULL)
{
CallsApplication *self = CALLS_APPLICATION (application);
// Later we will make provider loading/unloaded a dynamic
// process but that will have far-reaching consequences and is
// of no use immediately so for now, we just load one provider
// at startup. We can't put this in the actual startup() method
// though, because we need to be able to set the provider name
// from the command line and we use actions to do that, which
// depend on the application already being started up.
if (!self->provider)
{
load_provider_plugin (self);
if (!self->provider)
{
g_application_quit (application);
return;
}
self->ringer = calls_ringer_new (self->provider);
g_assert (self->ringer != NULL);
}
/*
* We don't track the memory created. Ideally, we might have to.
* But we assume that the application is closed by closing the
* window. In that case, GTK+ frees the resources right.
*/
window = GTK_WINDOW (calls_main_window_new (gtk_app, self->provider));
calls_call_window_new (gtk_app, self->provider);
}
gtk_window_present (window);
}
static void
constructed (GObject *object)
{
GObjectClass *parent_class = g_type_class_peek (GTK_TYPE_APPLICATION);
CallsApplication *self = CALLS_APPLICATION (object);
GSimpleActionGroup *action_group;
action_group = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (action_group),
actions, G_N_ELEMENTS (actions), self);
g_object_unref (action_group);
parent_class->constructed (object);
}
static void
dispose (GObject *object)
{
@ -68,51 +282,20 @@ dispose (GObject *object)
g_clear_object (&self->ringer);
g_clear_object (&self->provider);
G_OBJECT_CLASS (calls_application_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
CallsApplication *self = (CallsApplication *)object;
g_string_free (self->provider_name, TRUE);
G_OBJECT_CLASS (calls_application_parent_class)->finalize (object);
}
static void
startup (GApplication *application)
{
CallsApplication *self = (CallsApplication *)application;
g_autoptr(GError) error = NULL;
G_APPLICATION_CLASS (calls_application_parent_class)->startup (application);
g_set_prgname (APP_ID);
g_set_application_name (_("Calls"));
self->provider = CALLS_PROVIDER (calls_mm_provider_new ());
g_assert (self->provider != NULL);
self->ringer = calls_ringer_new (self->provider);
g_assert (self->ringer != NULL);
}
static void
activate (GApplication *application)
{
CallsApplication *self = (CallsApplication *)application;
GtkApplication *app = (GtkApplication *)application;
GtkWindow *window;
g_assert (GTK_IS_APPLICATION (app));
window = gtk_application_get_active_window (app);
if (window == NULL)
{
/*
* We don't track the memory created. Ideally, we might have to.
* But we assume that the application is closed by closing the
* window. In that case, GTK+ frees the resources right.
*/
window = GTK_WINDOW (calls_main_window_new (app, self->provider));
calls_call_window_new (app, self->provider);
}
gtk_window_present (window);
}
static void
calls_application_class_init (CallsApplicationClass *klass)
@ -120,8 +303,11 @@ calls_application_class_init (CallsApplicationClass *klass)
GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
application_class->handle_local_options = handle_local_options;
application_class->startup = startup;
application_class->activate = activate;
@ -133,11 +319,28 @@ calls_application_class_init (CallsApplicationClass *klass)
g_type_ensure (HDY_TYPE_DIALER);
}
static void
calls_application_init (CallsApplication *self)
{
const GOptionEntry options[] = {
{
"provider", 'p', G_OPTION_FLAG_NONE,
G_OPTION_ARG_STRING, NULL,
_("The name of the plugin to use for the call Provider"),
_("PLUGIN")
},
{
NULL
}
};
g_application_add_main_option_entries (G_APPLICATION (self), options);
self->provider_name = g_string_new (DEFAULT_PROVIDER_PLUGIN);
}
CallsApplication *
calls_application_new (void)
{

View File

@ -23,12 +23,13 @@
gnome = import('gnome')
src_include = include_directories('.')
calls_deps = [ dependency('gobject-2.0'),
dependency('gtk+-3.0'),
dependency('libhandy-0.0'),
dependency('ModemManager'),
dependency('mm-glib'),
dependency('gsound'),
dependency('libpeas-1.0'),
]
calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
@ -37,12 +38,6 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
'calls-provider.c', 'calls-provider.h',
'calls-enumerate-params.c', 'calls-enumerate-params.h',
'calls-enumerate.c', 'calls-enumerate.h',
'calls-ofono-call.c', 'calls-ofono-call.h',
'calls-ofono-origin.c', 'calls-ofono-origin.h',
'calls-ofono-provider.c', 'calls-ofono-provider.h',
'calls-mm-call.c', 'calls-mm-call.h',
'calls-mm-origin.c', 'calls-mm-origin.h',
'calls-mm-provider.c', 'calls-mm-provider.h',
'calls-party.c', 'calls-party.h',
'calls-call-data.c', 'calls-call-data.h',
'calls-call-holder.c', 'calls-call-holder.h',
@ -60,23 +55,12 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
'util.c', 'util.h',
])
calls_dummy_sources = files(['calls-dummy-call.c', 'calls-dummy-call.h',
'calls-dummy-origin.c', 'calls-dummy-origin.h',
'calls-dummy-provider.c', 'calls-dummy-provider.h',
])
prefix = get_option('prefix')
config_data = configuration_data()
config_data.set_quoted('APP_ID', calls_id)
config_data.set_quoted('GETTEXT_PACKAGE', calls_name)
config_data.set_quoted('LOCALEDIR', join_paths(prefix, get_option('localedir')))
config_data.set_quoted('PACKAGE_URL', calls_homepage)
config_data.set_quoted('PACKAGE_VERSION', calls_version)
config_data.set_quoted('VCS_TAG', '@VCS_TAG@')
calls_config_data = config_data
calls_config_data.set_quoted('VCS_TAG', '@VCS_TAG@')
config_h_in = configure_file(
output: 'config.h.in',
configuration: config_data
configuration: calls_config_data
)
config_h = vcs_tag(
@ -99,6 +83,6 @@ calls_resources = gnome.compile_resources(
executable('calls',
calls_sources, calls_enum_sources, calls_resources, 'main.c',
dependencies : calls_deps,
link_with : gdbofono_lib,
export_dynamic : true,
include_directories : include_directories('..'),
install : true)

View File

@ -11,6 +11,7 @@ test_env = [
test_cflags = [
'-fPIE',
'-DFOR_TESTING'
]
test_link_args = [
@ -38,13 +39,16 @@ foreach test : tests
'common.h' ]
t = executable(name, test_sources,
calls_sources, calls_dummy_sources, calls_enum_sources, calls_resources,
calls_sources, dummy_sources, calls_enum_sources, calls_resources,
c_args : test_cflags,
link_args: test_link_args,
link_with : gdbofono_lib,
dependencies: calls_deps,
include_directories : include_directories('..',
join_paths('..', 'src')),
include_directories : [
top_include,
src_include,
dummy_include
]
)
test(name, t, env: test_env)
endforeach

View File

@ -7,7 +7,7 @@
#include "setup-provider.h"
#include "calls-dummy-origin.h"
#define TEST_ORIGIN_NAME "Test origin"
#define TEST_ORIGIN_NAME "Dummy origin"
#define TEST_CALL_NUMBER "0123456789"

View File

@ -43,14 +43,9 @@ test_dummy_provider_origins (ProviderFixture *fixture,
{
GList *origins;
calls_dummy_provider_add_origin (fixture->dummy_provider,
"Test origin 1");
calls_dummy_provider_add_origin (fixture->dummy_provider,
"Test origin 2");
origins = calls_provider_get_origins
(CALLS_PROVIDER (fixture->dummy_provider));
g_assert_cmpuint (g_list_length (origins), ==, 2);
g_assert_cmpuint (g_list_length (origins), ==, 1);
g_list_free (origins);
}