From 281ccbd8e6b57d9240c6a53c4153b275eab21e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Tue, 19 May 2020 12:36:29 +0200 Subject: [PATCH] Show notification on missed calls Closes: #153 --- src/calls-application.c | 6 ++ src/calls-notifier.c | 224 ++++++++++++++++++++++++++++++++++++++++ src/calls-notifier.h | 42 ++++++++ src/meson.build | 1 + 4 files changed, 273 insertions(+) create mode 100644 src/calls-notifier.c create mode 100644 src/calls-notifier.h diff --git a/src/calls-application.c b/src/calls-application.c index 170ee10..b5fd94e 100644 --- a/src/calls-application.c +++ b/src/calls-application.c @@ -30,6 +30,7 @@ #include "calls-new-call-box.h" #include "calls-encryption-indicator.h" #include "calls-ringer.h" +#include "calls-notifier.h" #include "calls-record-store.h" #include "calls-contacts.h" #include "calls-call-window.h" @@ -59,6 +60,7 @@ struct _CallsApplication gboolean daemon; CallsManager *manager; CallsRinger *ringer; + CallsNotifier *notifier; CallsRecordStore *record_store; CallsContacts *contacts; CallsMainWindow *main_window; @@ -340,6 +342,9 @@ start_proper (CallsApplication *self) self->contacts = calls_contacts_new (); g_assert (self->contacts != NULL); + self->notifier = calls_notifier_new (self->contacts); + g_assert (CALLS_IS_NOTIFIER (self->notifier)); + self->main_window = calls_main_window_new (gtk_app, G_LIST_MODEL (self->record_store), @@ -473,6 +478,7 @@ finalize (GObject *object) g_clear_object (&self->main_window); g_clear_object (&self->record_store); g_clear_object (&self->ringer); + g_clear_object (&self->notifier); G_OBJECT_CLASS (calls_application_parent_class)->dispose (object); } diff --git a/src/calls-notifier.c b/src/calls-notifier.c new file mode 100644 index 0000000..dd4b2e9 --- /dev/null +++ b/src/calls-notifier.c @@ -0,0 +1,224 @@ +/* + * 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 . + * + * Author: Guido Günther + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "calls-notifier.h" +#include "calls-manager.h" +#include "config.h" + +#include +#include + +enum { + PROP_0, + PROP_CONTACTS, + PROP_LAST_PROP, +}; +static GParamSpec *props[PROP_LAST_PROP]; + +struct _CallsNotifier +{ + GObject parent_instance; + + GListStore *unanswered; + GList *notifications; + + CallsContacts *contacts; +}; + +G_DEFINE_TYPE (CallsNotifier, calls_notifier, G_TYPE_OBJECT); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (EPhoneNumber, e_phone_number_free) + +static void +notify (CallsNotifier *self, CallsCall *call) +{ + GApplication *app = g_application_get_default (); + g_autoptr(GNotification) notification; + g_autofree gchar *msg = NULL; + g_autofree gchar *ref = NULL; + g_autoptr(EPhoneNumber) phone_number = NULL; + g_autoptr(GError) err = NULL; + const char *number, *name; + CallsBestMatch *match; + + notification = g_notification_new (_("Missed call")); + + number = calls_call_get_number (call); + if (!number) + goto done; + + phone_number = e_phone_number_from_string (number, NULL, &err); + if (!phone_number) + { + g_warning ("Failed to convert %s to a phone number: %s", number, err->message); + goto done; + } + + match = calls_contacts_lookup_phone_number (self->contacts, phone_number); + if (!match) + goto done; + + name = calls_best_match_get_name (match); + if (name) + msg = g_strdup_printf (_("Missed call from %s"), name); + + done: + if (msg == NULL) + msg = g_strdup_printf (_("Missed call from unknown caller")); + + g_notification_set_body (notification, msg); + ref = g_strdup_printf ("missed-call-%s", calls_call_get_number (call) ?: "unknown"); + g_application_send_notification (app, ref, notification); +} + + +static void +state_changed_cb (CallsNotifier *self, + CallsCallState new_state, + CallsCallState old_state, + CallsCall *call) +{ + guint n; + + g_return_if_fail (CALLS_IS_NOTIFIER (self)); + g_return_if_fail (CALLS_IS_CALL (call)); + g_return_if_fail (old_state != new_state); + + if (old_state == CALLS_CALL_STATE_INCOMING && + new_state == CALLS_CALL_STATE_DISCONNECTED) + { + notify (self, call); + } + + /* Can use g_list_store_find with newer glib */ + n = g_list_model_get_n_items (G_LIST_MODEL (self->unanswered)); + for (int i = 0; i < n; i++) + { + g_autoptr(CallsCall) item = g_list_model_get_item (G_LIST_MODEL (self->unanswered), i); + if (item == call) + { + g_list_store_remove (self->unanswered, i); + g_signal_handlers_disconnect_by_data (item, self); + } + } +} + +static void +call_added_cb (CallsNotifier *self, CallsCall *call) +{ + g_list_store_append(self->unanswered, call); + + g_signal_connect_swapped (call, + "state-changed", + G_CALLBACK (state_changed_cb), + self); +} + + +static void +calls_notifier_init (CallsNotifier *self) +{ + self->unanswered = g_list_store_new (CALLS_TYPE_CALL); +} + + +static void +calls_notifier_constructed (GObject *object) +{ + g_autoptr (GList) calls = NULL; + GList *c; + CallsNotifier *self = CALLS_NOTIFIER (object); + + g_signal_connect_swapped (calls_manager_get_default (), + "call-add", + G_CALLBACK (call_added_cb), + self); + + calls = calls_manager_get_calls (calls_manager_get_default ()); + for (c = calls; c != NULL; c = c->next) + { + call_added_cb (self, c->data); + } + + G_OBJECT_CLASS (calls_notifier_parent_class)->constructed (object); +} + + +static void +calls_notifier_dispose (GObject *object) +{ + CallsNotifier *self = CALLS_NOTIFIER (object); + + g_list_store_remove_all (self->unanswered); + g_clear_object (&self->unanswered); + g_clear_object (&self->contacts); + + G_OBJECT_CLASS (calls_notifier_parent_class)->dispose (object); +} + +static void +calls_notifier_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CallsNotifier *self = CALLS_NOTIFIER (object); + + switch (property_id) + { + case PROP_CONTACTS: + g_set_object (&self->contacts, + CALLS_CONTACTS (g_value_get_object (value))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +calls_notifier_class_init (CallsNotifierClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = calls_notifier_constructed; + object_class->dispose = calls_notifier_dispose; + object_class->set_property = calls_notifier_set_property; + + props[PROP_CONTACTS] = + g_param_spec_object ("contacts", + "Contacts", + "Interface for libfolks", + CALLS_TYPE_CONTACTS, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + +CallsNotifier * +calls_notifier_new (CallsContacts *contacts) +{ + return g_object_new (CALLS_TYPE_NOTIFIER, "contacts", contacts, NULL); +} diff --git a/src/calls-notifier.h b/src/calls-notifier.h new file mode 100644 index 0000000..b440387 --- /dev/null +++ b/src/calls-notifier.h @@ -0,0 +1,42 @@ +/* + * 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 . + * + * Author: Guido Günther + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef CALLS_NOTIFIER_H__ +#define CALLS_NOTIFIER_H__ + +#include "calls-contacts.h" + +#include + +G_BEGIN_DECLS + +#define CALLS_TYPE_NOTIFIER (calls_notifier_get_type ()) + +G_DECLARE_FINAL_TYPE (CallsNotifier, calls_notifier, CALLS, NOTIFIER, GObject); + +CallsNotifier *calls_notifier_new (CallsContacts *contacts); + +G_END_DECLS + +#endif /* CALLS_NOTIFIER_H__ */ diff --git a/src/meson.build b/src/meson.build index b89a0c8..ead0e6c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -88,6 +88,7 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h', 'calls-best-match.c', 'calls-best-match.h', 'calls-in-app-notification.c', 'calls-in-app-notification.h', 'calls-manager.c', 'calls-manager.h', + 'calls-notifier.c', 'calls-notifier.h', 'contrib/hdy-avatar.c', 'contrib/hdy-avatar.h', ])