diff --git a/debian/control b/debian/control
index 19521fc..8b2da4b 100644
--- a/debian/control
+++ b/debian/control
@@ -12,6 +12,8 @@ Build-Depends:
libpeas-dev,
libgom-1.0-dev,
libebook-contacts1.2-dev,
+ valac,
+ libfolks-dev,
meson,
pkg-config,
# to run the tests
diff --git a/meson.build b/meson.build
index a37ede5..0cd9cb1 100644
--- a/meson.build
+++ b/meson.build
@@ -21,7 +21,7 @@
project(
'calls',
- 'c',
+ 'c', 'vala',
version: '0.1.0',
license: 'GPLv3+',
meson_version: '>= 0.47.0',
diff --git a/src/calls-best-match-view.vala b/src/calls-best-match-view.vala
new file mode 100644
index 0000000..c396c69
--- /dev/null
+++ b/src/calls-best-match-view.vala
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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 .
+ *
+ * Author: Bob Ham
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+
+public class Calls.BestMatchView : Folks.SearchView
+{
+ public Folks.Individual? best_match
+ {
+ get;
+ private set;
+ }
+
+ private void update ()
+ {
+ best_match =
+ individuals.is_empty ? null : individuals.first ();
+ }
+
+ public BestMatchView (Folks.IndividualAggregator aggregator,
+ Folks.Query query)
+ {
+ base (aggregator, query);
+
+ this.individuals_changed_detailed.connect
+ ((sv, p) => { update (); });
+ update();
+ }
+}
diff --git a/src/calls-contacts.c b/src/calls-contacts.c
new file mode 100644
index 0000000..e1fe73b
--- /dev/null
+++ b/src/calls-contacts.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 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 .
+ *
+ * Author: Bob Ham
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#include "calls-contacts.h"
+
+#include
+
+
+struct _CallsContacts
+{
+ GObject parent_instance;
+
+ FolksIndividualAggregator *big_pile_of_contacts;
+ /** Map of call target (EPhoneNumber) to CallsBestMatchView */
+ GHashTable *phone_number_views;
+};
+
+G_DEFINE_TYPE (CallsContacts, calls_contacts, G_TYPE_OBJECT);
+
+
+static guint
+phone_number_hash (const EPhoneNumber *number)
+{
+ g_autofree gchar *str = NULL;
+
+ str = e_phone_number_to_string
+ (number, E_PHONE_NUMBER_FORMAT_E164);
+ g_assert (str != NULL);
+
+ return g_str_hash (str);
+}
+
+
+static gboolean
+phone_number_equal (const EPhoneNumber *a,
+ const EPhoneNumber *b)
+{
+ return
+ e_phone_number_compare (a, b)
+ == E_PHONE_NUMBER_MATCH_EXACT;
+}
+
+
+static void
+prepare_cb (FolksIndividualAggregator *aggregator,
+ GAsyncResult *res,
+ CallsContacts *self)
+{
+ GError *error = NULL;
+ folks_individual_aggregator_prepare_finish (aggregator,
+ res,
+ &error);
+ if (error)
+ {
+ g_warning ("Error preparing Folks individual aggregator: %s",
+ error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_debug ("Folks individual aggregator prepared");
+ }
+}
+
+
+static void
+constructed (GObject *object)
+{
+ CallsContacts *self = CALLS_CONTACTS (object);
+
+ self->big_pile_of_contacts = folks_individual_aggregator_dup ();
+ g_assert (self->big_pile_of_contacts != NULL);
+ g_object_ref (self->big_pile_of_contacts);
+
+ folks_individual_aggregator_prepare (self->big_pile_of_contacts,
+ (GAsyncReadyCallback)prepare_cb,
+ self);
+
+ self->phone_number_views = g_hash_table_new_full
+ ((GHashFunc)phone_number_hash,
+ (GEqualFunc)phone_number_equal,
+ (GDestroyNotify)e_phone_number_free,
+ g_object_unref);
+
+ G_OBJECT_CLASS (calls_contacts_parent_class)->constructed (object);
+}
+
+
+static void
+dispose (GObject *object)
+{
+ CallsContacts *self = CALLS_CONTACTS (object);
+
+ if (self->phone_number_views)
+ {
+ g_hash_table_unref (self->phone_number_views);
+ self->phone_number_views = NULL;
+ }
+
+ g_clear_object (&self->big_pile_of_contacts);
+
+ G_OBJECT_CLASS (calls_contacts_parent_class)->dispose (object);
+}
+
+
+static void
+calls_contacts_class_init (CallsContactsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+}
+
+
+static void
+calls_contacts_init (CallsContacts *self)
+{
+}
+
+
+CallsContacts *
+calls_contacts_new ()
+{
+ return g_object_new (CALLS_TYPE_CONTACTS, NULL);
+}
+
+
+static void
+search_view_prepare_cb (FolksSearchView *view,
+ GAsyncResult *res,
+ CallsContacts *self)
+{
+ GError *error = NULL;
+
+ folks_search_view_prepare_finish (view, res, &error);
+
+ if (error)
+ {
+ g_warning ("Error preparing Folks search view: %s",
+ error->message);
+ g_error_free (error);
+ }
+}
+
+
+CallsBestMatchView *
+calls_contacts_lookup_phone_number (CallsContacts *self,
+ EPhoneNumber *number)
+{
+ CallsBestMatchView *view;
+ CallsPhoneNumberQuery *query;
+
+ view = g_hash_table_lookup (self->phone_number_views, number);
+ if (view)
+ {
+ return view;
+ }
+
+ query = calls_phone_number_query_new (number);
+ if (!query)
+ {
+ return NULL;
+ }
+
+ view = calls_best_match_view_new
+ (self->big_pile_of_contacts, FOLKS_QUERY (query));
+ g_object_unref (query);
+
+ folks_search_view_prepare
+ (FOLKS_SEARCH_VIEW (view),
+ (GAsyncReadyCallback)search_view_prepare_cb,
+ self);
+
+ g_hash_table_insert (self->phone_number_views,
+ e_phone_number_copy (number),
+ view);
+
+ return view;
+}
diff --git a/src/calls-contacts.h b/src/calls-contacts.h
new file mode 100644
index 0000000..57fe837
--- /dev/null
+++ b/src/calls-contacts.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018, 2019 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 .
+ *
+ * Author: Bob Ham
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef CALLS_CONTACTS_H__
+#define CALLS_CONTACTS_H__
+
+#include "calls-vala.h"
+
+#include
+#include
+
+
+G_BEGIN_DECLS
+
+#define CALLS_TYPE_CONTACTS (calls_contacts_get_type ())
+
+G_DECLARE_FINAL_TYPE (CallsContacts, calls_contacts, CALLS, CONTACTS, GObject);
+
+CallsContacts * calls_contacts_new ();
+CallsBestMatchView * calls_contacts_lookup_phone_number (CallsContacts *self,
+ EPhoneNumber *number);
+
+G_END_DECLS
+
+#endif /* CALLS_CONTACTS_H__ */
diff --git a/src/calls-phone-number-query.vala b/src/calls-phone-number-query.vala
new file mode 100644
index 0000000..e8f0e77
--- /dev/null
+++ b/src/calls-phone-number-query.vala
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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 .
+ *
+ * Author: Bob Ham
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+
+public class Calls.PhoneNumberQuery : Folks.Query
+{
+ private E.PhoneNumber _number;
+
+ public PhoneNumberQuery (E.PhoneNumber number)
+ {
+ string[] match_fields =
+ { Folks.PersonaStore.detail_key (Folks.PersonaDetail.PHONE_NUMBERS) };
+
+ Object (match_fields: match_fields);
+
+ this._number = number;
+ }
+
+ public override uint is_match (Folks.Individual individual)
+ {
+ const uint MATCH_MAX = 4;
+
+ // Iterate over the set of phone numbers
+ Gee.Iterator iter =
+ individual.phone_numbers.iterator ();
+ uint match = 0;
+ while (match < MATCH_MAX && iter.next ())
+ {
+ // Get the phone number
+ Folks.PhoneFieldDetails details = iter.get ();
+ string indiv_number = details.value;
+
+ // Parse it
+ E.PhoneNumber indiv_parsed;
+ try
+ {
+ indiv_parsed =
+ E.PhoneNumber.from_string (indiv_number, null);
+ }
+ catch (GLib.Error e)
+ {
+ warning ("Error parsing Folks phone number `%s'" +
+ " for Individual `%s': %s",
+ indiv_number,
+ individual.display_name,
+ e.message);
+ continue;
+ }
+
+ // Compare the Individual's and query's numbers
+ E.PhoneNumberMatch result =
+ indiv_parsed.compare (this._number);
+
+ uint this_match;
+ switch (result)
+ {
+ case E.PhoneNumberMatch.NONE: this_match = 0; break;
+ case E.PhoneNumberMatch.SHORT: this_match = 0; break;
+ case E.PhoneNumberMatch.NATIONAL: this_match = 1; break;
+ case E.PhoneNumberMatch.EXACT: this_match = MATCH_MAX; break;
+ default: this_match = 0; break;
+ }
+
+ if (this_match > match)
+ {
+ match = this_match;
+ }
+ }
+
+ return match;
+ }
+}
diff --git a/src/meson.build b/src/meson.build
index 6e9ccd7..9ed4463 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -35,6 +35,7 @@ calls_deps = [ dependency('gobject-2.0'),
dependency('libpeas-1.0'),
dependency('gom-1.0'),
dependency('libebook-contacts-1.2'),
+ dependency('folks'),
]
if wl_scanner.found()
@@ -42,6 +43,27 @@ if wl_scanner.found()
calls_deps += dependency('wayland-client', version: '>=1.14')
endif
+
+calls_vala_deps = [
+ dependency('libebook-contacts-1.2'),
+ dependency('folks'),
+]
+
+calls_vala_sources = files (
+ [
+ 'calls-phone-number-query.vala',
+ 'calls-best-match-view.vala',
+ ]
+)
+
+calls_vala = static_library (
+ 'calls-vala',
+ calls_vala_sources,
+ vala_header : 'calls-vala.h',
+ dependencies : calls_vala_deps,
+)
+
+
calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
'calls-call.c',
'calls-origin.c', 'calls-origin.h',
@@ -64,6 +86,7 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h',
'calls-call-record.c', 'calls-call-record.h',
'calls-record-store.c', 'calls-record-store.h',
'calls-call-record-row.c', 'calls-call-record-row.h',
+ 'calls-contacts.c', 'calls-contacts.h',
])
calls_config_data = config_data
@@ -92,10 +115,13 @@ calls_resources = gnome.compile_resources(
c_name: 'call',
)
-executable('calls',
- calls_sources, calls_enum_sources, calls_resources,
- wl_proto_sources, wayland_sources, 'main.c',
- dependencies : calls_deps,
- export_dynamic : true,
- include_directories : calls_includes,
- install : true)
+executable (
+ 'calls',
+ calls_sources, calls_enum_sources, calls_resources,
+ wl_proto_sources, wayland_sources, 'main.c',
+ dependencies : calls_deps,
+ export_dynamic : true,
+ include_directories : calls_includes,
+ install : true,
+ link_with : calls_vala
+)
diff --git a/tests/meson.build b/tests/meson.build
index e5a934d..14df0f8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -43,7 +43,7 @@ foreach test : tests
wl_proto_sources, wayland_sources,
c_args : test_cflags,
link_args: test_link_args,
- link_with : gdbofono_lib,
+ link_with : calls_vala,
dependencies: calls_deps,
include_directories : [
calls_includes,