LCOV - code coverage report
Current view: top level - src/plugins/notification - valent-notification-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 79.9 % 458 366
Test Date: 2024-03-22 06:22:29 Functions: 90.6 % 32 29
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 44.5 % 362 161

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-notification-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gtk/gtk.h>
       9                 :             : #include <libportal/portal.h>
      10                 :             : #include <gio/gio.h>
      11                 :             : #include <json-glib/json-glib.h>
      12                 :             : #include <valent.h>
      13                 :             : 
      14                 :             : #include "valent-notification-dialog.h"
      15                 :             : #include "valent-notification-plugin.h"
      16                 :             : #include "valent-notification-upload.h"
      17                 :             : 
      18                 :             : #define DEFAULT_ICON_SIZE 512
      19                 :             : 
      20                 :             : 
      21                 :             : struct _ValentNotificationPlugin
      22                 :             : {
      23                 :             :   ValentDevicePlugin   parent_instance;
      24                 :             : 
      25                 :             :   GCancellable        *cancellable;
      26                 :             :   ValentNotifications *notifications;
      27                 :             :   ValentSession       *session;
      28                 :             : 
      29                 :             :   GHashTable          *cache;
      30                 :             :   GHashTable          *dialogs;
      31                 :             :   unsigned int         notifications_watch : 1;
      32                 :             : };
      33                 :             : 
      34   [ +  +  +  - ]:          88 : G_DEFINE_FINAL_TYPE (ValentNotificationPlugin, valent_notification_plugin, VALENT_TYPE_DEVICE_PLUGIN)
      35                 :             : 
      36                 :             : static void valent_notification_plugin_handle_notification         (ValentNotificationPlugin *self,
      37                 :             :                                                                     JsonNode                 *packet);
      38                 :             : static void valent_notification_plugin_handle_notification_action  (ValentNotificationPlugin *self,
      39                 :             :                                                                     JsonNode                 *packet);
      40                 :             : static void valent_notification_plugin_handle_notification_reply   (ValentNotificationPlugin *self,
      41                 :             :                                                                     JsonNode                 *packet);
      42                 :             : static void valent_notification_plugin_handle_notification_request (ValentNotificationPlugin *self,
      43                 :             :                                                                     JsonNode                 *packet);
      44                 :             : 
      45                 :             : static void valent_notification_plugin_close_notification          (ValentNotificationPlugin *self,
      46                 :             :                                                                     const char               *id);
      47                 :             : static void valent_notification_plugin_request_notifications       (ValentNotificationPlugin *self);
      48                 :             : static void valent_notification_plugin_send_notification           (ValentNotificationPlugin *self,
      49                 :             :                                                                     const char               *id,
      50                 :             :                                                                     const char               *appName,
      51                 :             :                                                                     const char               *title,
      52                 :             :                                                                     const char               *body,
      53                 :             :                                                                     GIcon                    *icon);
      54                 :             : static void valent_notification_plugin_show_notification           (ValentNotificationPlugin *self,
      55                 :             :                                                                     JsonNode                 *packet,
      56                 :             :                                                                     GIcon                    *gicon);
      57                 :             : 
      58                 :             : 
      59                 :             : /*
      60                 :             :  * ValentNotifications Callbacks
      61                 :             :  */
      62                 :             : static void
      63                 :           5 : on_notification_added (ValentNotifications      *listener,
      64                 :             :                        ValentNotification       *notification,
      65                 :             :                        ValentNotificationPlugin *self)
      66                 :             : {
      67                 :           5 :   GSettings *settings;
      68                 :           5 :   const char *application;
      69                 :          10 :   g_auto (GStrv) deny = NULL;
      70                 :             : 
      71         [ +  - ]:           5 :   g_assert (VALENT_IS_NOTIFICATIONS (listener));
      72         [ -  + ]:           5 :   g_assert (VALENT_IS_NOTIFICATION (notification));
      73         [ -  + ]:           5 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
      74                 :             : 
      75                 :           5 :   settings = valent_extension_get_settings (VALENT_EXTENSION (self));
      76                 :             : 
      77         [ +  - ]:           5 :   if (!g_settings_get_boolean (settings, "forward-notifications"))
      78                 :             :     return;
      79                 :             : 
      80   [ -  +  -  - ]:           5 :   if (!g_settings_get_boolean (settings, "forward-when-active") &&
      81                 :           0 :       valent_session_get_active (self->session))
      82                 :             :     return;
      83                 :             : 
      84                 :           5 :   application = valent_notification_get_application (notification);
      85                 :           5 :   deny = g_settings_get_strv (settings, "forward-deny");
      86                 :             : 
      87   [ +  +  -  + ]:           5 :   if (application && g_strv_contains ((const char * const *)deny, application))
      88         [ #  # ]:           0 :     return;
      89                 :             : 
      90         [ +  - ]:           5 :   valent_notification_plugin_send_notification (self,
      91                 :             :                                                 valent_notification_get_id (notification),
      92                 :             :                                                 valent_notification_get_application (notification),
      93                 :             :                                                 valent_notification_get_title (notification),
      94                 :             :                                                 valent_notification_get_body (notification),
      95                 :             :                                                 valent_notification_get_icon (notification));
      96                 :             : }
      97                 :             : 
      98                 :             : static void
      99                 :           1 : on_notification_removed (ValentNotifications      *notifications,
     100                 :             :                          const char               *id,
     101                 :             :                          ValentNotificationPlugin *self)
     102                 :             : {
     103         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATIONS (notifications));
     104         [ -  + ]:           1 :   g_assert (id != NULL);
     105                 :             : 
     106                 :           1 :   valent_notification_plugin_close_notification (self, id);
     107                 :           1 : }
     108                 :             : 
     109                 :             : static void
     110                 :          26 : valent_notification_plugin_watch_notifications (ValentNotificationPlugin *self,
     111                 :             :                                                 gboolean                  watch)
     112                 :             : {
     113                 :          26 :   ValentNotifications *notifications = valent_notifications_get_default ();
     114                 :             : 
     115         [ +  - ]:          26 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     116                 :             : 
     117         [ +  + ]:          26 :   if (self->notifications_watch == watch)
     118                 :             :     return;
     119                 :             : 
     120         [ +  + ]:          10 :   if (watch)
     121                 :             :     {
     122                 :           5 :       g_signal_connect_object (notifications,
     123                 :             :                                "notification-added",
     124                 :             :                                G_CALLBACK (on_notification_added),
     125                 :             :                                self, 0);
     126                 :           5 :       g_signal_connect_object (notifications,
     127                 :             :                                "notification-removed",
     128                 :             :                                G_CALLBACK (on_notification_removed),
     129                 :             :                                self, 0);
     130                 :           5 :       self->notifications_watch = TRUE;
     131                 :             :     }
     132                 :             :   else
     133                 :             :     {
     134                 :           5 :       g_signal_handlers_disconnect_by_data (notifications, self);
     135                 :           5 :       self->notifications_watch = FALSE;
     136                 :             :     }
     137                 :             : }
     138                 :             : 
     139                 :             : /*
     140                 :             :  * Icon Transfers
     141                 :             :  */
     142                 :             : typedef struct
     143                 :             : {
     144                 :             :   GRecMutex     mutex;
     145                 :             :   ValentDevice *device;
     146                 :             :   JsonNode     *packet;
     147                 :             : } IconTransferData;
     148                 :             : 
     149                 :             : static void
     150                 :           1 : icon_transfer_data_free (gpointer data)
     151                 :             : {
     152                 :           1 :   IconTransferData *transfer = (IconTransferData *)data;
     153                 :             : 
     154                 :           1 :   g_rec_mutex_lock (&transfer->mutex);
     155         [ -  + ]:           1 :   g_clear_object (&transfer->device);
     156         [ -  + ]:           1 :   g_clear_pointer (&transfer->packet, json_node_unref);
     157                 :           1 :   g_rec_mutex_unlock (&transfer->mutex);
     158                 :           1 :   g_rec_mutex_clear (&transfer->mutex);
     159                 :           1 :   g_clear_pointer (&transfer, g_free);
     160                 :           1 : }
     161                 :             : 
     162                 :             : static GFile *
     163                 :           1 : valent_notification_plugin_get_icon_file (ValentNotificationPlugin *self,
     164                 :             :                                           JsonNode                 *packet)
     165                 :             : {
     166                 :           2 :   g_autoptr (GFile) file = NULL;
     167                 :           1 :   const char *payload_hash;
     168                 :             : 
     169         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     170         [ -  + ]:           1 :   g_assert (VALENT_IS_PACKET (packet));
     171                 :             : 
     172         [ +  - ]:           1 :   if (valent_packet_get_string (packet, "payloadHash", &payload_hash))
     173                 :             :     {
     174                 :           1 :       ValentContext *context = NULL;
     175                 :             : 
     176                 :           1 :       context = valent_extension_get_context (VALENT_EXTENSION (self));
     177                 :           1 :       file = valent_context_get_cache_file (context, payload_hash);
     178                 :             :     }
     179                 :             :   else
     180                 :             :     {
     181                 :           0 :       g_autoptr (GFileIOStream) stream = NULL;
     182                 :             : 
     183         [ #  # ]:           0 :       file = g_file_new_tmp ("valent-notification-icon.XXXXXX", &stream, NULL);
     184                 :             :     }
     185                 :             : 
     186                 :           1 :   return g_steal_pointer (&file);
     187                 :             : }
     188                 :             : 
     189                 :             : static void
     190                 :           1 : download_icon_task (GTask        *task,
     191                 :             :                     gpointer      source_object,
     192                 :             :                     gpointer      task_data,
     193                 :             :                     GCancellable *cancellable)
     194                 :             : {
     195                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (source_object);
     196                 :           1 :   IconTransferData *transfer = (IconTransferData *)task_data;
     197                 :           1 :   g_autoptr (ValentDevice) device = NULL;
     198   [ +  -  -  - ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     199   [ +  -  -  - ]:           1 :   g_autoptr (GFile) file = NULL;
     200                 :           1 :   GError *error = NULL;
     201                 :             : 
     202         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     203         [ -  + ]:           1 :   g_assert (transfer != NULL);
     204                 :             : 
     205         [ +  - ]:           1 :   if (g_task_return_error_if_cancelled (task))
     206                 :             :     return;
     207                 :             : 
     208                 :           1 :   g_rec_mutex_lock (&transfer->mutex);
     209                 :           1 :   device = g_steal_pointer (&transfer->device);
     210                 :           1 :   packet = g_steal_pointer (&transfer->packet);
     211                 :           1 :   g_rec_mutex_unlock (&transfer->mutex);
     212                 :             : 
     213                 :           1 :   file = valent_notification_plugin_get_icon_file (self, packet);
     214                 :             : 
     215                 :             :   /* Check if we've already downloaded this icon */
     216         [ +  - ]:           1 :   if (!g_file_query_exists (file, cancellable))
     217                 :             :     {
     218         [ -  - ]:           1 :       g_autoptr (GIOStream) source = NULL;
     219         [ -  - ]:           1 :       g_autoptr (GFileOutputStream) target = NULL;
     220         [ -  - ]:           1 :       g_autoptr (GFile) cache_dir = NULL;
     221   [ +  -  -  - ]:           1 :       g_autoptr (ValentChannel) channel = NULL;
     222                 :             : 
     223                 :             :       /* Ensure the cache directory exists */
     224                 :           1 :       cache_dir = g_file_get_parent (file);
     225                 :             : 
     226         [ -  + ]:           1 :       if (g_mkdir_with_parents (g_file_peek_path (cache_dir), 0700) != 0)
     227                 :             :         {
     228                 :           0 :           return g_task_return_new_error (task,
     229                 :             :                                           G_IO_ERROR,
     230                 :             :                                           G_IO_ERROR_FAILED,
     231                 :             :                                           "Error: %s",
     232                 :           0 :                                           g_strerror (errno));
     233                 :             :         }
     234                 :             : 
     235                 :             :       /* Get the device channel */
     236         [ -  + ]:           1 :       if ((channel = valent_device_ref_channel (device)) == NULL)
     237                 :             :         {
     238                 :           0 :           return g_task_return_new_error (task,
     239                 :             :                                           G_IO_ERROR,
     240                 :             :                                           G_IO_ERROR_NOT_CONNECTED,
     241                 :             :                                           "Device is disconnected");
     242                 :             :         }
     243                 :             : 
     244                 :           1 :       source = valent_channel_download (channel, packet, cancellable, &error);
     245                 :             : 
     246         [ -  + ]:           1 :       if (source == NULL)
     247                 :             :         {
     248                 :           0 :           g_file_delete (file, NULL, NULL);
     249                 :           0 :           return g_task_return_error (task, error);
     250                 :             :         }
     251                 :             : 
     252                 :             :       /* Get the output stream */
     253                 :           1 :       target = g_file_replace (file,
     254                 :             :                                NULL,
     255                 :             :                                FALSE,
     256                 :             :                                G_FILE_CREATE_REPLACE_DESTINATION,
     257                 :             :                                cancellable,
     258                 :             :                                &error);
     259                 :             : 
     260         [ -  + ]:           1 :       if (target == NULL)
     261                 :             :         {
     262                 :           0 :           g_file_delete (file, NULL, NULL);
     263                 :           0 :           return g_task_return_error (task, error);
     264                 :             :         }
     265                 :             : 
     266                 :             :       /* Start download */
     267                 :           1 :       g_output_stream_splice (G_OUTPUT_STREAM (target),
     268                 :             :                               g_io_stream_get_input_stream (source),
     269                 :             :                               (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
     270                 :             :                                G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET),
     271                 :             :                               cancellable,
     272                 :             :                               &error);
     273                 :             : 
     274         [ -  + ]:           1 :       if (error != NULL)
     275                 :             :         {
     276                 :           0 :           g_file_delete (file, NULL, NULL);
     277                 :           0 :           return g_task_return_error (task, error);
     278                 :             :         }
     279                 :             :     }
     280                 :             : 
     281                 :             :   /* If we're in a sandbox, send the file as a GBytesIcon in case the file path
     282                 :             :    * is not valid for the host system. */
     283         [ -  + ]:           1 :   if (xdp_portal_running_under_sandbox ())
     284                 :             :     {
     285                 :           0 :       g_autoptr (GBytes) bytes = NULL;
     286                 :             : 
     287                 :           0 :       bytes = g_file_load_bytes (file, cancellable, NULL, &error);
     288                 :             : 
     289         [ #  # ]:           0 :       if (bytes == NULL)
     290                 :             :         {
     291                 :           0 :           g_file_delete (file, NULL, NULL);
     292                 :           0 :           return g_task_return_error (task, error);
     293                 :             :         }
     294                 :             : 
     295                 :           0 :       g_task_return_pointer (task, g_bytes_icon_new (bytes), g_object_unref);
     296                 :             :     }
     297                 :             :   else
     298                 :             :     {
     299                 :           1 :       g_task_return_pointer (task, g_file_icon_new (file), g_object_unref);
     300                 :             :     }
     301                 :             : }
     302                 :             : 
     303                 :             : static void
     304                 :           1 : valent_notification_plugin_download_icon (ValentNotificationPlugin *self,
     305                 :             :                                           JsonNode                 *packet,
     306                 :             :                                           GCancellable             *cancellable,
     307                 :             :                                           GAsyncReadyCallback       callback,
     308                 :             :                                           gpointer                  user_data)
     309                 :             : {
     310                 :           1 :   ValentExtension *extension = VALENT_EXTENSION (self);
     311                 :           2 :   g_autoptr (GTask) task = NULL;
     312                 :           1 :   IconTransferData *transfer = NULL;
     313                 :             : 
     314         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     315         [ -  + ]:           1 :   g_assert (VALENT_IS_PACKET (packet));
     316   [ +  -  +  -  :           1 :   g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
             -  +  -  - ]
     317                 :             : 
     318                 :           1 :   transfer = g_new0 (IconTransferData, 1);
     319                 :           1 :   g_rec_mutex_init (&transfer->mutex);
     320                 :           1 :   g_rec_mutex_lock (&transfer->mutex);
     321                 :           1 :   transfer->device = g_object_ref (valent_extension_get_object (extension));
     322                 :           1 :   transfer->packet = json_node_ref (packet);
     323                 :           1 :   g_rec_mutex_unlock (&transfer->mutex);
     324                 :             : 
     325                 :           1 :   task = g_task_new (self, cancellable, callback, user_data);
     326         [ +  - ]:           1 :   g_task_set_source_tag (task, valent_notification_plugin_download_icon);
     327                 :           1 :   g_task_set_task_data (task, transfer, icon_transfer_data_free);
     328         [ +  - ]:           1 :   g_task_run_in_thread (task, download_icon_task);
     329                 :           1 : }
     330                 :             : 
     331                 :             : static GIcon *
     332                 :           1 : valent_notification_plugin_download_icon_finish (ValentNotificationPlugin  *self,
     333                 :             :                                                  GAsyncResult              *result,
     334                 :             :                                                  GError                   **error)
     335                 :             : {
     336         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     337         [ -  + ]:           1 :   g_assert (g_task_is_valid (result, self));
     338   [ +  -  -  + ]:           1 :   g_assert (error == NULL || *error == NULL);
     339                 :             : 
     340                 :           1 :   return g_task_propagate_pointer (G_TASK (result), error);
     341                 :             : }
     342                 :             : 
     343                 :             : /*
     344                 :             :  * Remote Notifications
     345                 :             :  */
     346                 :             : static void
     347                 :           4 : valent_notification_plugin_show_notification (ValentNotificationPlugin *self,
     348                 :             :                                               JsonNode                 *packet,
     349                 :             :                                               GIcon                    *gicon)
     350                 :             : {
     351                 :           4 :   ValentDevice *device;
     352                 :           4 :   g_autoptr (GNotification) notification = NULL;
     353         [ +  - ]:           4 :   g_autoptr (GIcon) icon = NULL;
     354   [ +  +  -  - ]:           4 :   g_auto (GStrv) ticker_strv = NULL;
     355                 :           4 :   const char *id;
     356                 :           4 :   const char *app_name = NULL;
     357                 :           4 :   const char *title = NULL;
     358                 :           4 :   const char *text = NULL;
     359                 :           4 :   const char *ticker;
     360                 :           4 :   const char *reply_id;
     361                 :           4 :   JsonArray *actions;
     362                 :             : 
     363                 :             :   /* Finish the icon task */
     364   [ +  +  +  -  :           4 :   if (G_IS_ICON (gicon))
             +  -  +  - ]
     365                 :           1 :     icon = g_object_ref (gicon);
     366                 :             : 
     367                 :             :   /* Ensure we have a notification id */
     368         [ -  + ]:           4 :   if (!valent_packet_get_string (packet, "id", &id))
     369                 :             :     {
     370                 :           0 :       g_debug ("%s(): expected \"id\" field holding a string",
     371                 :             :                G_STRFUNC);
     372                 :           0 :       return;
     373                 :             :     }
     374                 :             : 
     375                 :             :   /* This should never be absent, but we check anyways */
     376         [ -  + ]:           4 :   if (!valent_packet_get_string (packet, "appName", &app_name))
     377                 :             :     {
     378                 :           0 :       g_debug ("%s(): expected \"appName\" field holding a string",
     379                 :             :                G_STRFUNC);
     380                 :           0 :       return;
     381                 :             :     }
     382                 :             : 
     383                 :             :   /* If `title` or `text` are missing, try to compose them from `ticker` */
     384   [ +  -  +  - ]:           8 :   if ((!valent_packet_get_string (packet, "title", &title) ||
     385         [ -  - ]:           4 :        !valent_packet_get_string (packet, "text", &text)) &&
     386                 :           0 :       valent_packet_get_string (packet, "ticker", &ticker))
     387                 :             :     {
     388                 :           0 :       ticker_strv = g_strsplit (ticker, ": ", 2);
     389                 :           0 :       title = ticker_strv[0];
     390                 :           0 :       text = ticker_strv[1];
     391                 :             :     }
     392                 :             : 
     393   [ +  -  -  + ]:           4 :   if (title == NULL || text == NULL)
     394                 :             :     {
     395                 :           0 :       g_debug ("%s(): expected either title and text, or ticker",
     396                 :             :                G_STRFUNC);
     397         [ #  # ]:           0 :       return;
     398                 :             :     }
     399                 :             : 
     400                 :           4 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     401                 :             : 
     402                 :             :   /* Start building the GNotification */
     403                 :           4 :   notification = g_notification_new (title);
     404                 :             : 
     405                 :             :   /* Repliable Notification */
     406         [ +  + ]:           4 :   if (valent_packet_get_string (packet, "requestReplyId", &reply_id))
     407                 :             :     {
     408                 :           1 :       g_autoptr (ValentNotification) incoming = NULL;
     409                 :           1 :       const char *time_str = NULL;
     410                 :           1 :       int64_t time = 0;
     411                 :           1 :       GVariant *target;
     412                 :             : 
     413         [ +  - ]:           1 :       if (valent_packet_get_string (packet, "time", &time_str))
     414                 :           1 :         time = g_ascii_strtoll (time_str, NULL, 10);
     415                 :             : 
     416                 :           1 :       incoming = g_object_new (VALENT_TYPE_NOTIFICATION,
     417                 :             :                                "id",          id,
     418                 :             :                                "application", app_name,
     419                 :             :                                "icon",        icon,
     420                 :             :                                "title",       title,
     421                 :             :                                "body",        text,
     422                 :             :                                "time",        time,
     423                 :             :                                NULL);
     424                 :           1 :       target = g_variant_new ("(ssv)",
     425                 :             :                               reply_id,
     426                 :             :                               "",
     427                 :             :                               valent_notification_serialize (incoming));
     428                 :             : 
     429         [ +  - ]:           1 :       valent_notification_set_device_action (notification,
     430                 :             :                                              device,
     431                 :             :                                              "notification.reply",
     432                 :             :                                              target);
     433                 :             :     }
     434                 :             : 
     435                 :             :   /* Notification Actions */
     436         [ +  + ]:           4 :   if (valent_packet_get_array (packet, "actions", &actions))
     437                 :             :     {
     438                 :           1 :       unsigned int n_actions;
     439                 :             : 
     440                 :           1 :       n_actions = json_array_get_length (actions);
     441                 :             : 
     442         [ +  + ]:           4 :       for (unsigned int i = 0; i < n_actions; i++)
     443                 :             :         {
     444                 :           3 :           JsonNode *element;
     445                 :           3 :           const char *action;
     446                 :           3 :           GVariant *target;
     447                 :             : 
     448   [ +  -  -  + ]:           6 :           if ((element = json_array_get_element (actions, i)) == NULL ||
     449                 :           3 :               json_node_get_value_type (element) != G_TYPE_STRING)
     450                 :           0 :             continue;
     451                 :             : 
     452                 :           3 :           action = json_node_get_string (element);
     453                 :           3 :           target = g_variant_new ("(ss)", id, action);
     454                 :           3 :           valent_notification_add_device_button (notification,
     455                 :             :                                                  device,
     456                 :             :                                                  action,
     457                 :             :                                                  "notification.action",
     458                 :             :                                                  target);
     459                 :             :         }
     460                 :             :     }
     461                 :             : 
     462                 :             :   /* Special cases.
     463                 :             :    *
     464                 :             :    * In some cases, usually on Android, a notification "type" can be inferred
     465                 :             :    * from the ID string or the appName is duplicated as the title.
     466                 :             :    */
     467         [ -  + ]:           4 :   if (g_strrstr (id, "MissedCall") != NULL)
     468                 :             :     {
     469                 :           0 :       g_notification_set_title (notification, title);
     470                 :           0 :       g_notification_set_body (notification, text);
     471                 :             : 
     472         [ #  # ]:           0 :       if (icon == NULL)
     473                 :           0 :         icon = g_themed_icon_new ("call-missed-symbolic");
     474                 :             :     }
     475         [ -  + ]:           4 :   else if (g_strrstr (id, "sms") != NULL)
     476                 :             :     {
     477                 :           0 :       g_notification_set_title (notification, title);
     478                 :           0 :       g_notification_set_body (notification, text);
     479                 :             : 
     480         [ #  # ]:           0 :       if (icon == NULL)
     481                 :           0 :         icon = g_themed_icon_new ("sms-symbolic");
     482                 :             :     }
     483         [ -  + ]:           4 :   else if (g_strcmp0 (app_name, title) == 0)
     484                 :             :     {
     485                 :           0 :       g_notification_set_title (notification, title);
     486                 :           0 :       g_notification_set_body (notification, text);
     487                 :             :     }
     488                 :             :   else
     489                 :             :     {
     490                 :           4 :       g_autofree char *ticker_body = NULL;
     491                 :             : 
     492                 :           4 :       ticker_body = g_strdup_printf ("%s: %s", title, text);
     493                 :           4 :       g_notification_set_title (notification, app_name);
     494                 :           4 :       g_notification_set_body (notification, ticker_body);
     495                 :             :     }
     496                 :             : 
     497         [ +  + ]:           4 :   if (icon != NULL)
     498                 :           1 :     g_notification_set_icon (notification, icon);
     499                 :             : 
     500         [ -  + ]:           4 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     501                 :             :                                           id,
     502                 :             :                                           notification);
     503                 :             : }
     504                 :             : 
     505                 :             : static void
     506                 :           1 : valent_notification_plugin_download_icon_cb (ValentNotificationPlugin *self,
     507                 :             :                                              GAsyncResult             *result,
     508                 :             :                                              gpointer                  user_data)
     509                 :             : {
     510                 :           1 :   g_autoptr (JsonNode) packet = user_data;
     511   [ -  -  +  - ]:           1 :   g_autoptr (GIcon) icon = NULL;
     512   [ -  -  +  - ]:           1 :   g_autoptr (GError) error = NULL;
     513                 :             : 
     514         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     515         [ -  + ]:           1 :   g_assert (g_task_is_valid (result, self));
     516                 :             : 
     517                 :           1 :   icon = valent_notification_plugin_download_icon_finish (self, result, &error);
     518                 :             : 
     519         [ -  + ]:           1 :   if (icon == NULL)
     520                 :             :     {
     521                 :             :       /* If the operation was cancelled, the plugin is being disposed */
     522         [ #  # ]:           0 :       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     523         [ #  # ]:           0 :         return;
     524                 :             : 
     525                 :           0 :       g_warning ("Downloading icon: %s", error->message);
     526                 :             :     }
     527                 :             : 
     528         [ -  + ]:           1 :   valent_notification_plugin_show_notification (self, packet, icon);
     529                 :             : }
     530                 :             : 
     531                 :             : static void
     532                 :           4 : valent_notification_plugin_handle_notification (ValentNotificationPlugin *self,
     533                 :             :                                                 JsonNode                 *packet)
     534                 :             : {
     535                 :           4 :   const char *id;
     536                 :             : 
     537         [ +  - ]:           4 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     538         [ -  + ]:           4 :   g_assert (VALENT_IS_PACKET (packet));
     539                 :             : 
     540         [ -  + ]:           4 :   if (!valent_packet_get_string (packet, "id", &id))
     541                 :             :     {
     542                 :           0 :       g_debug ("%s(): expected \"id\" field holding a string",
     543                 :             :                G_STRFUNC);
     544                 :           0 :       return;
     545                 :             :     }
     546                 :             : 
     547                 :             :   /* A report that a remote notification has been dismissed */
     548         [ -  + ]:           4 :   if (valent_packet_check_field (packet, "isCancel"))
     549                 :             :     {
     550                 :           0 :       g_hash_table_remove (self->cache, id);
     551                 :           0 :       valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     552                 :           0 :       return;
     553                 :             :     }
     554                 :             : 
     555                 :             :   /* A notification that should only be shown once, already existed on the
     556                 :             :    * device, and is already in the cache. This typically means the device just
     557                 :             :    * re-connected and is re-sending known notifications. */
     558   [ -  +  -  - ]:           4 :   if (valent_packet_check_field (packet, "onlyOnce") &&
     559         [ #  # ]:           0 :       valent_packet_check_field (packet, "silent") &&
     560                 :           0 :       g_hash_table_contains (self->cache, id))
     561                 :             :     {
     562                 :             :       VALENT_NOTE ("skipping existing notification: %s", id);
     563                 :             :       return;
     564                 :             :     }
     565                 :             : 
     566         [ -  + ]:           4 :   g_hash_table_replace (self->cache,
     567                 :           4 :                         g_strdup (id),
     568                 :           4 :                         json_node_ref (packet));
     569                 :             : 
     570         [ +  + ]:           4 :   if (valent_packet_has_payload (packet))
     571                 :             :     {
     572                 :           1 :       valent_notification_plugin_download_icon (self,
     573                 :             :                                                 packet,
     574                 :             :                                                 self->cancellable,
     575                 :             :                                                 (GAsyncReadyCallback)valent_notification_plugin_download_icon_cb,
     576                 :           1 :                                                 json_node_ref (packet));
     577                 :             :     }
     578                 :             :   else
     579                 :             :     {
     580                 :           3 :       valent_notification_plugin_show_notification (self, packet, NULL);
     581                 :             :     }
     582                 :             : }
     583                 :             : 
     584                 :             : static void
     585                 :           0 : valent_notification_plugin_handle_notification_action (ValentNotificationPlugin *self,
     586                 :             :                                                        JsonNode                 *packet)
     587                 :             : {
     588         [ #  # ]:           0 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     589         [ #  # ]:           0 :   g_assert (VALENT_IS_PACKET (packet));
     590                 :             : 
     591                 :           0 :   VALENT_NOTE ("TODO: kdeconnect.notification.action");
     592                 :           0 : }
     593                 :             : 
     594                 :             : static void
     595                 :           0 : valent_notification_plugin_handle_notification_reply (ValentNotificationPlugin *self,
     596                 :             :                                                       JsonNode                 *packet)
     597                 :             : {
     598         [ #  # ]:           0 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     599         [ #  # ]:           0 :   g_assert (VALENT_IS_PACKET (packet));
     600                 :             : 
     601                 :           0 :   VALENT_NOTE ("TODO: kdeconnect.notification.reply");
     602                 :           0 : }
     603                 :             : 
     604                 :             : static void
     605                 :           0 : valent_notification_plugin_handle_notification_request (ValentNotificationPlugin *self,
     606                 :             :                                                         JsonNode                 *packet)
     607                 :             : {
     608         [ #  # ]:           0 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     609         [ #  # ]:           0 :   g_assert (VALENT_IS_PACKET (packet));
     610                 :             : 
     611                 :           0 :   VALENT_NOTE ("TODO: kdeconnect.notification.request");
     612                 :           0 : }
     613                 :             : 
     614                 :             : static void
     615                 :           5 : valent_notification_plugin_request_notifications (ValentNotificationPlugin *self)
     616                 :             : {
     617                 :          10 :   g_autoptr (JsonBuilder) builder = NULL;
     618         [ -  + ]:           5 :   g_autoptr (JsonNode) packet = NULL;
     619                 :             : 
     620         [ +  - ]:           5 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     621                 :             : 
     622                 :           5 :   valent_packet_init (&builder, "kdeconnect.notification.request");
     623                 :           5 :   json_builder_set_member_name (builder, "request");
     624                 :           5 :   json_builder_add_boolean_value (builder, TRUE);
     625                 :           5 :   packet = valent_packet_end (&builder);
     626                 :             : 
     627         [ +  - ]:           5 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     628                 :           5 : }
     629                 :             : 
     630                 :             : static void
     631                 :           2 : valent_notification_plugin_close_notification (ValentNotificationPlugin *self,
     632                 :             :                                                const char               *id)
     633                 :             : {
     634                 :           4 :   g_autoptr (JsonBuilder) builder = NULL;
     635         [ -  + ]:           2 :   g_autoptr (JsonNode) packet = NULL;
     636                 :             : 
     637         [ +  - ]:           2 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     638         [ -  + ]:           2 :   g_assert (id != NULL);
     639                 :             : 
     640                 :           2 :   valent_packet_init (&builder, "kdeconnect.notification.request");
     641                 :           2 :   json_builder_set_member_name (builder, "cancel");
     642                 :           2 :   json_builder_add_string_value (builder, id);
     643                 :           2 :   packet = valent_packet_end (&builder);
     644                 :             : 
     645         [ +  - ]:           2 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     646                 :           2 : }
     647                 :             : 
     648                 :             : static void
     649                 :           4 : valent_notification_upload_execute_cb (GObject      *object,
     650                 :             :                                        GAsyncResult *result,
     651                 :             :                                        gpointer      user_data)
     652                 :             : {
     653                 :           4 :   ValentTransfer *transfer = VALENT_TRANSFER (object);
     654                 :           8 :   g_autoptr (GError) error = NULL;
     655                 :             : 
     656   [ -  +  -  - ]:           4 :   if (!valent_transfer_execute_finish (transfer, result, &error) &&
     657                 :           0 :       !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     658                 :             :     {
     659                 :           0 :       g_autoptr (ValentDevice) device = NULL;
     660         [ #  # ]:           0 :       g_autoptr (JsonNode) packet = NULL;
     661                 :             : 
     662                 :           0 :       g_object_get (transfer,
     663                 :             :                     "device", &device,
     664                 :             :                     "packet", &packet,
     665                 :             :                     NULL);
     666         [ #  # ]:           0 :       valent_device_send_packet (device, packet, NULL, NULL, NULL);
     667                 :             :     }
     668                 :           4 : }
     669                 :             : 
     670                 :             : static void
     671                 :           4 : valent_notification_plugin_send_notification_with_icon (ValentNotificationPlugin *self,
     672                 :             :                                                         JsonNode                 *packet,
     673                 :             :                                                         GIcon                    *icon)
     674                 :             : {
     675         [ +  - ]:           4 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     676         [ -  + ]:           4 :   g_assert (VALENT_IS_PACKET (packet));
     677   [ +  -  +  -  :           4 :   g_assert (icon == NULL || G_IS_ICON (icon));
             +  -  -  + ]
     678                 :             : 
     679   [ +  -  +  -  :           4 :   if (G_IS_ICON (icon))
             +  -  +  - ]
     680                 :             :     {
     681                 :           4 :       ValentDevice *device;
     682                 :           8 :       g_autoptr (ValentTransfer) transfer = NULL;
     683                 :             : 
     684                 :           4 :       device = valent_extension_get_object (VALENT_EXTENSION (self));
     685                 :           4 :       transfer = valent_notification_upload_new (device, packet, icon);
     686                 :           4 :       valent_transfer_execute (transfer,
     687                 :             :                                NULL,
     688                 :             :                                valent_notification_upload_execute_cb,
     689                 :             :                                NULL);
     690         [ +  - ]:           4 :       return;
     691                 :             :     }
     692                 :             : 
     693                 :           0 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     694                 :             : }
     695                 :             : 
     696                 :             : /**
     697                 :             :  * valent_notification_plugin_send_notification:
     698                 :             :  * @self: a `ValentNotificationPlugin`
     699                 :             :  * @id: the notification id
     700                 :             :  * @appName: (nullable): the notifying application
     701                 :             :  * @title: (nullable): the notification title
     702                 :             :  * @body: (nullable): the notification body
     703                 :             :  * @icon: (nullable): a `GIcon`
     704                 :             :  *
     705                 :             :  * Send a notification to the remote device.
     706                 :             :  */
     707                 :             : static void
     708                 :           6 : valent_notification_plugin_send_notification (ValentNotificationPlugin *self,
     709                 :             :                                               const char               *id,
     710                 :             :                                               const char               *appName,
     711                 :             :                                               const char               *title,
     712                 :             :                                               const char               *body,
     713                 :             :                                               GIcon                    *icon)
     714                 :             : {
     715                 :           6 :   g_autoptr (JsonBuilder) builder = NULL;
     716   [ -  +  -  - ]:           6 :   g_autoptr (JsonNode) packet = NULL;
     717   [ +  -  -  - ]:           6 :   g_autofree char *ticker = NULL;
     718                 :             : 
     719         [ +  - ]:           6 :   g_return_if_fail (VALENT_IS_NOTIFICATION_PLUGIN (self));
     720         [ -  + ]:           6 :   g_return_if_fail (id != NULL);
     721   [ +  +  +  -  :           6 :   g_return_if_fail (icon == NULL || G_IS_ICON (icon));
             +  -  -  + ]
     722                 :             : 
     723                 :             :   /* Build packet */
     724                 :           6 :   valent_packet_init (&builder, "kdeconnect.notification");
     725                 :           6 :   json_builder_set_member_name (builder, "id");
     726                 :           6 :   json_builder_add_string_value (builder, id);
     727                 :             : 
     728                 :             :   /* Application Name */
     729                 :           6 :   json_builder_set_member_name (builder, "appName");
     730         [ +  + ]:           7 :   json_builder_add_string_value (builder, appName ? appName : "Valent");
     731                 :             : 
     732                 :             :   /* Title & Body (aka Ticker) */
     733                 :           6 :   json_builder_set_member_name (builder, "title");
     734         [ +  + ]:           7 :   json_builder_add_string_value (builder, title ? title : "");
     735                 :           6 :   json_builder_set_member_name (builder, "body");
     736         [ +  + ]:           7 :   json_builder_add_string_value (builder, body ? body : "");
     737                 :             : 
     738                 :           6 :   ticker = g_strdup_printf ("%s: %s", title, body);
     739                 :           6 :   json_builder_set_member_name (builder, "ticker");
     740                 :           6 :   json_builder_add_string_value (builder, ticker);
     741                 :             : 
     742                 :           6 :   packet = valent_packet_end (&builder);
     743                 :             : 
     744         [ +  + ]:           6 :   if (icon == NULL)
     745                 :           2 :     valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     746                 :             :   else
     747                 :           4 :     valent_notification_plugin_send_notification_with_icon (self, packet, icon);
     748                 :             : }
     749                 :             : 
     750                 :             : /*
     751                 :             :  * GActions
     752                 :             :  */
     753                 :             : static void
     754                 :           1 : notification_action_action (GSimpleAction *action,
     755                 :             :                             GVariant      *parameter,
     756                 :             :                             gpointer       user_data)
     757                 :             : {
     758                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (user_data);
     759                 :           2 :   g_autoptr (JsonBuilder) builder = NULL;
     760         [ -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     761                 :           1 :   char *id;
     762                 :           1 :   char *name;
     763                 :             : 
     764         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     765                 :             : 
     766                 :           1 :   g_variant_get (parameter, "(&s&s)", &id, &name);
     767                 :             : 
     768                 :           1 :   valent_packet_init (&builder, "kdeconnect.notification.action");
     769                 :           1 :   json_builder_set_member_name (builder, "key");
     770                 :           1 :   json_builder_add_string_value (builder, id);
     771                 :           1 :   json_builder_set_member_name (builder, "action");
     772                 :           1 :   json_builder_add_string_value (builder, name);
     773                 :           1 :   packet = valent_packet_end (&builder);
     774                 :             : 
     775         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     776                 :           1 : }
     777                 :             : 
     778                 :             : static void
     779                 :           1 : notification_cancel_action (GSimpleAction *action,
     780                 :             :                             GVariant      *parameter,
     781                 :             :                             gpointer       user_data)
     782                 :             : {
     783                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (user_data);
     784                 :           1 :   const char *id;
     785                 :           2 :   g_autoptr (JsonBuilder) builder = NULL;
     786         [ -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     787                 :             : 
     788         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     789                 :             : 
     790                 :           1 :   id = g_variant_get_string (parameter, NULL);
     791                 :             : 
     792                 :           1 :   valent_packet_init (&builder, "kdeconnect.notification");
     793                 :           1 :   json_builder_set_member_name (builder, "id");
     794                 :           1 :   json_builder_add_string_value (builder, id);
     795                 :           1 :   json_builder_set_member_name (builder, "isCancel");
     796                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     797                 :           1 :   packet = valent_packet_end (&builder);
     798                 :             : 
     799         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     800                 :           1 : }
     801                 :             : 
     802                 :             : static void
     803                 :           1 : notification_close_action (GSimpleAction *action,
     804                 :             :                            GVariant      *parameter,
     805                 :             :                            gpointer       user_data)
     806                 :             : {
     807                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (user_data);
     808                 :           1 :   const char *id;
     809                 :             : 
     810         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     811                 :             : 
     812                 :           1 :   id = g_variant_get_string (parameter, NULL);
     813                 :           1 :   valent_notification_plugin_close_notification (self, id);
     814                 :           1 : }
     815                 :             : 
     816                 :             : static void
     817                 :           1 : notification_reply_action (GSimpleAction *action,
     818                 :             :                            GVariant      *parameter,
     819                 :             :                            gpointer       user_data)
     820                 :             : {
     821                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (user_data);
     822                 :           1 :   const char *reply_id;
     823                 :           1 :   const char *message;
     824                 :           1 :   g_autoptr (GVariant) notificationv = NULL;
     825                 :             : 
     826         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     827                 :             : 
     828                 :           1 :   g_variant_get (parameter, "(&s&sv)", &reply_id, &message, &notificationv);
     829                 :             : 
     830                 :             :   /* If the reply ID is empty, we've received a broken request */
     831   [ +  -  -  + ]:           1 :   if (reply_id == NULL || *reply_id == '\0')
     832                 :             :     {
     833                 :           0 :       g_debug ("%s(): expected requestReplyId", G_STRFUNC);
     834                 :           0 :       return;
     835                 :             :     }
     836                 :             : 
     837                 :             :   /* If the message is empty, we're being asked to show the dialog */
     838   [ +  -  -  + ]:           1 :   if (message == NULL || *message == '\0')
     839                 :             :     {
     840         [ +  - ]:           1 :       g_autoptr (ValentNotificationDialog) dialog = NULL;
     841         [ #  # ]:           0 :       g_autoptr (ValentNotification) notification = NULL;
     842                 :             : 
     843         [ #  # ]:           0 :       if (!gtk_is_initialized ())
     844                 :             :         {
     845                 :           0 :           g_warning ("%s: No display available", G_STRFUNC);
     846                 :           0 :           return;
     847                 :             :         }
     848                 :             : 
     849                 :           0 :       notification = valent_notification_deserialize (notificationv);
     850                 :             : 
     851         [ #  # ]:           0 :       if ((dialog = g_hash_table_lookup (self->dialogs, notification)) == NULL)
     852                 :             :         {
     853                 :           0 :           ValentDevice *device;
     854                 :             : 
     855                 :           0 :           device = valent_extension_get_object (VALENT_EXTENSION (self));
     856                 :           0 :           dialog = g_object_new (VALENT_TYPE_NOTIFICATION_DIALOG,
     857                 :             :                                  "device",       device,
     858                 :             :                                  "notification", notification,
     859                 :             :                                  "reply-id",     reply_id,
     860                 :             :                                  NULL);
     861                 :           0 :           g_hash_table_insert (self->dialogs,
     862                 :             :                                g_object_ref (notification),
     863                 :             :                                g_object_ref_sink (dialog));
     864                 :             :         }
     865                 :             : 
     866         [ #  # ]:           0 :       gtk_window_present (GTK_WINDOW (dialog));
     867                 :             :     }
     868                 :             :   else
     869                 :             :     {
     870                 :           1 :       g_autoptr (JsonBuilder) builder = NULL;
     871         [ -  + ]:           1 :       g_autoptr (JsonNode) packet = NULL;
     872                 :             : 
     873                 :           1 :       valent_packet_init (&builder, "kdeconnect.notification.reply");
     874                 :           1 :       json_builder_set_member_name (builder, "requestReplyId");
     875                 :           1 :       json_builder_add_string_value (builder, reply_id);
     876                 :           1 :       json_builder_set_member_name (builder, "message");
     877                 :           1 :       json_builder_add_string_value (builder, message);
     878                 :           1 :       packet = valent_packet_end (&builder);
     879                 :             : 
     880         [ +  - ]:           1 :       valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     881                 :             :     }
     882                 :             : }
     883                 :             : 
     884                 :             : static void
     885                 :           1 : notification_send_action (GSimpleAction *action,
     886                 :             :                           GVariant      *parameter,
     887                 :             :                           gpointer       user_data)
     888                 :             : {
     889                 :           1 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (user_data);
     890                 :           1 :   GVariantDict dict;
     891                 :           2 :   g_autofree char *id = NULL;
     892                 :           1 :   const char *app = NULL;
     893                 :           1 :   const char *title = NULL;
     894                 :           1 :   const char *body = NULL;
     895                 :           1 :   g_autoptr (GVariant) iconv = NULL;
     896         [ +  - ]:           1 :   g_autoptr (GIcon) icon = NULL;
     897                 :             : 
     898         [ +  - ]:           1 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     899                 :             : 
     900                 :           1 :   g_variant_dict_init (&dict, parameter);
     901                 :             : 
     902                 :             :   /* Use a random ID as a fallback */
     903         [ -  + ]:           1 :   if (!g_variant_dict_lookup (&dict, "id", "s", &id))
     904                 :           0 :     id = g_uuid_string_random ();
     905                 :             : 
     906                 :           1 :   g_variant_dict_lookup (&dict, "application", "&s", &app);
     907                 :           1 :   g_variant_dict_lookup (&dict, "title", "&s", &title);
     908                 :           1 :   g_variant_dict_lookup (&dict, "body", "&s", &body);
     909                 :             : 
     910                 :             :   /* Check for a serialized GIcon */
     911                 :           1 :   iconv = g_variant_dict_lookup_value (&dict, "icon", G_VARIANT_TYPE ("(sv)"));
     912                 :             : 
     913         [ +  - ]:           1 :   if (iconv != NULL)
     914                 :           1 :     icon = g_icon_deserialize (iconv);
     915                 :             : 
     916                 :           1 :   valent_notification_plugin_send_notification (self, id, app, title, body, icon);
     917                 :             : 
     918         [ +  - ]:           1 :   g_variant_dict_clear (&dict);
     919                 :           1 : }
     920                 :             : 
     921                 :             : static const GActionEntry actions[] = {
     922                 :             :     {"action", notification_action_action, "(ss)",  NULL, NULL},
     923                 :             :     {"cancel", notification_cancel_action, "s",     NULL, NULL},
     924                 :             :     {"close",  notification_close_action,  "s",     NULL, NULL},
     925                 :             :     {"reply",  notification_reply_action,  "(ssv)", NULL, NULL},
     926                 :             :     {"send",   notification_send_action,   "a{sv}", NULL, NULL},
     927                 :             : };
     928                 :             : 
     929                 :             : /*
     930                 :             :  * ValentDevicePlugin
     931                 :             :  */
     932                 :             : static void
     933                 :          16 : valent_notification_plugin_update_state (ValentDevicePlugin *plugin,
     934                 :             :                                          ValentDeviceState   state)
     935                 :             : {
     936                 :          16 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (plugin);
     937                 :          16 :   gboolean available;
     938                 :             : 
     939         [ +  - ]:          16 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (self));
     940                 :             : 
     941                 :          16 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
     942                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
     943                 :             : 
     944                 :          16 :   valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available);
     945                 :          16 :   valent_notification_plugin_watch_notifications (self, available);
     946                 :             : 
     947                 :             :   /* Request Notifications */
     948         [ +  + ]:          16 :   if (available)
     949                 :             :     {
     950                 :           5 :       valent_notification_plugin_request_notifications (self);
     951                 :          16 :       VALENT_NOTE ("TODO: send active notifications");
     952                 :             :     }
     953                 :          16 : }
     954                 :             : 
     955                 :             : static void
     956                 :           4 : valent_notification_plugin_handle_packet (ValentDevicePlugin *plugin,
     957                 :             :                                           const char         *type,
     958                 :             :                                           JsonNode           *packet)
     959                 :             : {
     960                 :           4 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (plugin);
     961                 :             : 
     962         [ +  - ]:           4 :   g_assert (VALENT_IS_NOTIFICATION_PLUGIN (plugin));
     963         [ -  + ]:           4 :   g_assert (type != NULL);
     964         [ -  + ]:           4 :   g_assert (VALENT_IS_PACKET (packet));
     965                 :             : 
     966         [ +  - ]:           4 :   if (g_str_equal (type, "kdeconnect.notification"))
     967                 :           4 :     valent_notification_plugin_handle_notification (self, packet);
     968                 :             : 
     969         [ #  # ]:           0 :   else if (g_str_equal (type, "kdeconnect.notification.action"))
     970                 :           0 :     valent_notification_plugin_handle_notification_action (self, packet);
     971                 :             : 
     972         [ #  # ]:           0 :   else if (g_str_equal (type, "kdeconnect.notification.reply"))
     973                 :           0 :     valent_notification_plugin_handle_notification_reply (self, packet);
     974                 :             : 
     975         [ #  # ]:           0 :   else if (g_str_equal (type, "kdeconnect.notification.request"))
     976                 :           0 :     valent_notification_plugin_handle_notification_request (self, packet);
     977                 :             : 
     978                 :             :   else
     979                 :           0 :     g_assert_not_reached ();
     980                 :           4 : }
     981                 :             : 
     982                 :             : /*
     983                 :             :  * ValentObject
     984                 :             :  */
     985                 :             : static void
     986                 :          10 : valent_notification_plugin_destroy (ValentObject *object)
     987                 :             : {
     988                 :          10 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (object);
     989                 :             : 
     990                 :          10 :   valent_notification_plugin_watch_notifications (self, FALSE);
     991                 :             : 
     992                 :             :   /* Close any open reply dialogs */
     993         [ +  + ]:          10 :   g_clear_pointer (&self->cache, g_hash_table_unref);
     994         [ +  + ]:          10 :   g_clear_pointer (&self->dialogs, g_hash_table_unref);
     995                 :             : 
     996                 :             :   /* Cancel any pending operations */
     997                 :          10 :   g_cancellable_cancel (self->cancellable);
     998         [ +  + ]:          10 :   g_clear_object (&self->cancellable);
     999                 :             : 
    1000                 :          10 :   VALENT_OBJECT_CLASS (valent_notification_plugin_parent_class)->destroy (object);
    1001                 :          10 : }
    1002                 :             : 
    1003                 :             : /*
    1004                 :             :  * GObject
    1005                 :             :  */
    1006                 :             : static void
    1007                 :           5 : valent_notification_plugin_constructed (GObject *object)
    1008                 :             : {
    1009                 :           5 :   ValentNotificationPlugin *self = VALENT_NOTIFICATION_PLUGIN (object);
    1010                 :           5 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
    1011                 :             : 
    1012                 :           5 :   self->cancellable = g_cancellable_new ();
    1013                 :           5 :   self->notifications = valent_notifications_get_default();
    1014                 :           5 :   self->session = valent_session_get_default ();
    1015                 :             : 
    1016                 :           5 :   g_action_map_add_action_entries (G_ACTION_MAP (plugin),
    1017                 :             :                                    actions,
    1018                 :             :                                    G_N_ELEMENTS (actions),
    1019                 :             :                                    plugin);
    1020                 :             : 
    1021                 :           5 :   self->cache = g_hash_table_new_full (g_str_hash,
    1022                 :             :                                        g_str_equal,
    1023                 :             :                                        g_free,
    1024                 :             :                                        (GDestroyNotify)json_node_unref);
    1025                 :           5 :   self->dialogs = g_hash_table_new_full (valent_notification_hash,
    1026                 :             :                                          valent_notification_equal,
    1027                 :             :                                          g_object_unref,
    1028                 :             :                                          (GDestroyNotify)gtk_window_destroy);
    1029                 :             : 
    1030                 :           5 :   G_OBJECT_CLASS (valent_notification_plugin_parent_class)->constructed (object);
    1031                 :           5 : }
    1032                 :             : 
    1033                 :             : static void
    1034                 :           2 : valent_notification_plugin_class_init (ValentNotificationPluginClass *klass)
    1035                 :             : {
    1036                 :           2 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
    1037                 :           2 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
    1038                 :           2 :   ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
    1039                 :             : 
    1040                 :           2 :   object_class->constructed = valent_notification_plugin_constructed;
    1041                 :             : 
    1042                 :           2 :   vobject_class->destroy = valent_notification_plugin_destroy;
    1043                 :             : 
    1044                 :           2 :   plugin_class->handle_packet = valent_notification_plugin_handle_packet;
    1045                 :           2 :   plugin_class->update_state = valent_notification_plugin_update_state;
    1046                 :             : }
    1047                 :             : 
    1048                 :             : static void
    1049                 :           5 : valent_notification_plugin_init (ValentNotificationPlugin *self)
    1050                 :             : {
    1051                 :           5 : }
    1052                 :             : 
        

Generated by: LCOV version 2.0-1