diff --git a/po/POTFILES.in b/po/POTFILES.in index 04da0ae..fbd1b0b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -8,6 +8,7 @@ src/calls-account-overview.c src/calls-application.c src/calls-best-match.c src/calls-call-record-row.c +src/calls-emergency-call-types.c src/calls-main-window.c src/calls-notifier.c src/ui/account-overview.ui diff --git a/src/calls-emergency-call-types.c b/src/calls-emergency-call-types.c new file mode 100644 index 0000000..a987ff7 --- /dev/null +++ b/src/calls-emergency-call-types.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "CallsEmergencyCallType" + +#include "calls-emergency-call-types.h" + +#include + +/** + * SECTION:calls-emergency-call-type + * @short_description: Emergency call types per country + * + * Exerpt from https://source.android.com/docs/core/connect/emergency-number-db + * TODO: parse the actual database for dynamic updates and broader coverage + */ + +typedef struct { + char *number; + CallsEmergencyCallTypeFlags flags; +} CallsEmergencyNumber; + +typedef struct { + char *country_code; + CallsEmergencyNumber numbers[3]; +} CallsEmergencyNumberTypes; + +CallsEmergencyNumberTypes emergency_number_types[] = { + { "CH", + { + { "117", CALLS_EMERGENCY_CALL_TYPE_POLICE }, + { "144", CALLS_EMERGENCY_CALL_TYPE_AMBULANCE }, + { "118", CALLS_EMERGENCY_CALL_TYPE_FIRE_BRIGADE } + } + }, + { "DE", + { + { "112", (CALLS_EMERGENCY_CALL_TYPE_POLICE | + CALLS_EMERGENCY_CALL_TYPE_AMBULANCE | + CALLS_EMERGENCY_CALL_TYPE_FIRE_BRIGADE) } + } + }, + { "US", + { + { "911", (CALLS_EMERGENCY_CALL_TYPE_POLICE | + CALLS_EMERGENCY_CALL_TYPE_AMBULANCE | + CALLS_EMERGENCY_CALL_TYPE_FIRE_BRIGADE) } + } + } +}; + + +static char * +flags_to_string (CallsEmergencyCallTypeFlags flags) +{ + g_autoptr (GPtrArray) types = g_ptr_array_new (); + + if (flags & CALLS_EMERGENCY_CALL_TYPE_POLICE) { + g_ptr_array_add (types, _("Police")); + } + if (flags & CALLS_EMERGENCY_CALL_TYPE_AMBULANCE) { + g_ptr_array_add (types, _("Ambulance")); + } + if (flags & CALLS_EMERGENCY_CALL_TYPE_FIRE_BRIGADE) { + g_ptr_array_add (types, _("Fire Brigade")); + } + if (flags & CALLS_EMERGENCY_CALL_TYPE_MOUNTAIN_RESCUE) { + g_ptr_array_add (types, _("Mountain Rescue")); + } + + if (types->len == 0) + return NULL; + + g_ptr_array_add (types, NULL); + /* TODO: join in RTL and locale aware way */ + return g_strjoinv (", ", (GStrv)types->pdata); +} + + +char * +calls_emergency_call_type_get_name (const char *lookup, const char *country_code) +{ + g_return_val_if_fail (lookup, NULL); + if (country_code == NULL) + return NULL; + + for (int i = 0; i < G_N_ELEMENTS (emergency_number_types); i++){ + CallsEmergencyNumberTypes *numbers = &emergency_number_types[i]; + + if (g_str_equal (numbers->country_code, country_code) == FALSE) + continue; + + for (int n = 0; n < G_N_ELEMENTS (numbers->numbers); n++) { + CallsEmergencyNumber *number = &numbers->numbers[n]; + + if (g_strcmp0 (lookup, number->number) == 0) + return flags_to_string (number->flags); + } + } + + return NULL; +} diff --git a/src/calls-emergency-call-types.h b/src/calls-emergency-call-types.h new file mode 100644 index 0000000..67aba4d --- /dev/null +++ b/src/calls-emergency-call-types.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 Guido Günther + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +# pragma once + +typedef enum { + CALLS_EMERGENCY_CONTACT_SOURCE_UNKNOWN = 0, + CALLS_EMERGENCY_CONTACT_SOURCE_FALLBACK = (1 << 0), + CALLS_EMERGENCY_CONTACT_SOURCE_SIM = (1 << 1), +} CallsEmergencyContactSource; + +/* See 3GPP TS 22.101 version 14.8.0 Release 14, Chapter 10.1 */ +typedef enum { + CALLS_EMERGENCY_CALL_TYPE_UNKNOWN = 0, + CALLS_EMERGENCY_CALL_TYPE_POLICE = (1 << 0), + CALLS_EMERGENCY_CALL_TYPE_AMBULANCE = (1 << 1), + CALLS_EMERGENCY_CALL_TYPE_FIRE_BRIGADE = (1 << 2), + CALLS_EMERGENCY_CALL_TYPE_MOUNTAIN_RESCUE = (1 << 3), +} CallsEmergencyCallTypeFlags; + +char *calls_emergency_call_type_get_name (const char *number, const char *country_code); diff --git a/src/calls-emergency-calls-manager.c b/src/calls-emergency-calls-manager.c index ca414cb..9462247 100644 --- a/src/calls-emergency-calls-manager.c +++ b/src/calls-emergency-calls-manager.c @@ -9,6 +9,7 @@ #define G_LOG_DOMAIN "CallsEmergencyCallsManger" #include "calls-emergency-calls-manager.h" +#include "calls-emergency-call-types.h" #include "calls-origin.h" #include "calls-manager.h" @@ -29,7 +30,6 @@ typedef struct _CallsEmergencyCallsManager } CallsEmergencyCallsManger; static void calls_emergency_calls_iface_init (CallsDBusEmergencyCallsIface *iface); - G_DEFINE_TYPE_WITH_CODE (CallsEmergencyCallsManager, calls_emergency_calls_manager, CALLS_DBUS_TYPE_EMERGENCY_CALLS_SKELETON, @@ -90,18 +90,26 @@ handle_get_emergency_contacts (CallsDBusEmergencyCalls *object, for (int i = 0; i < g_list_model_get_n_items (self->origins); i++) { CallsOrigin *origin = g_list_model_get_item (self->origins, i); g_auto (GStrv) emergency_numbers = NULL; + const char *country_code; emergency_numbers = calls_origin_get_emergency_numbers (origin); if (!emergency_numbers) continue; + country_code = calls_origin_get_country_code (origin); for (int j = 0; j < g_strv_length (emergency_numbers); j++) { + g_autofree char *contact = NULL; + g_variant_builder_open (&contacts_builder, G_VARIANT_TYPE (CONTACT_FORMAT)); g_variant_builder_add (&contacts_builder, "s", emergency_numbers[j]); - /* For non-addressbook numbers we just use the number itself as contact */ - g_variant_builder_add (&contacts_builder, "s", emergency_numbers[j]); + + contact = calls_emergency_call_type_get_name (emergency_numbers[j], country_code); + if (contact == NULL) + contact = g_strdup (emergency_numbers[j]); + g_variant_builder_add (&contacts_builder, "s", contact); /* Currently unused */ - g_variant_builder_add (&contacts_builder, "i", 0); + g_variant_builder_add (&contacts_builder, "i", + CALLS_EMERGENCY_CONTACT_SOURCE_UNKNOWN); /* Currently no hints */ g_variant_builder_add (&contacts_builder, "a{sv}", NULL); g_variant_builder_close (&contacts_builder); diff --git a/src/meson.build b/src/meson.build index 421f399..5d64ba5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -102,6 +102,7 @@ calls_sources = files([ 'calls-contacts-row.c', 'calls-contacts-row.h', 'calls-dbus-manager.c', 'calls-dbus-manager.h', 'calls-emergency-calls-manager.c', 'calls-emergency-calls-manager.h', + 'calls-emergency-call-types.c', 'calls-emergency-call-types.h', 'calls-history-box.c', 'calls-history-box.h', 'calls-in-app-notification.c', 'calls-in-app-notification.h', 'calls-log.c', 'calls-log.h', diff --git a/tests/meson.build b/tests/meson.build index 5ddd4f4..29f072e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -38,6 +38,20 @@ test_includes = include_directories('.') subdir('mock') subdir('services') +test_sources = [ 'test-emergency-call-types.c' ] +t = executable('emergency-call-types', test_sources, + c_args : test_cflags, + link_args: test_link_args, + pie: true, + link_with : [calls_vala, libcalls], + dependencies: calls_deps, + include_directories : [ + calls_includes, + ] + ) +test('emergency-call-types', t, env: test_env) + + test_sources = [ 'test-manager.c' ] t = executable('manager', test_sources, diff --git a/tests/test-emergency-call-types.c b/tests/test-emergency-call-types.c new file mode 100644 index 0000000..18ff4eb --- /dev/null +++ b/tests/test-emergency-call-types.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) Guido Günther + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "calls-emergency-call-types.h" + +#include + +static void +test_lookup (void) +{ + char *lookup = NULL; + + /* No countyr code -> no match */ + lookup = calls_emergency_call_type_get_name ("112", NULL); + g_assert_null (lookup); + + /* Country code that's not in the table */ + lookup = calls_emergency_call_type_get_name ("112", "doesnotexist"); + g_assert_null (lookup); + + /* Numbers that match a single type */ + lookup = calls_emergency_call_type_get_name ("117", "CH"); + g_assert_cmpstr (lookup, ==, "Police"); + g_free (lookup); + + lookup = calls_emergency_call_type_get_name ("144", "CH"); + g_assert_cmpstr (lookup, ==, "Ambulance"); + g_free (lookup); + + /* Numbers that match multiple types */ + lookup = calls_emergency_call_type_get_name ("112", "DE"); + g_assert_cmpstr (lookup, ==, "Police, Ambulance, Fire Brigade"); + g_free (lookup); + + /* Numbers that doesn't match */ + lookup = calls_emergency_call_type_get_name ("123456", "DE"); + g_assert_null (lookup); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/Calls/EmergencyCallTypes/lookup", test_lookup); + + return g_test_run (); +}