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,