LCOV - code coverage report
Current view: top level - src/libvalent/device - valent-device-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 92.3 % 117 108
Test Date: 2024-05-12 19:03:18 Functions: 100.0 % 15 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 50.6 % 158 80

             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-device-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gio/gio.h>
       9                 :             : #include <json-glib/json-glib.h>
      10                 :             : #include <libpeas.h>
      11                 :             : #include <libvalent-core.h>
      12                 :             : 
      13                 :             : #include "valent-device.h"
      14                 :             : #include "valent-device-plugin.h"
      15                 :             : #include "valent-packet.h"
      16                 :             : 
      17                 :             : #define PLUGIN_SETTINGS_KEY "X-DevicePluginSettings"
      18                 :             : 
      19                 :             : 
      20                 :             : /**
      21                 :             :  * ValentDevicePlugin:
      22                 :             :  *
      23                 :             :  * An abstract base class for device plugins.
      24                 :             :  *
      25                 :             :  * `ValentDevicePlugin` is a base class for plugins that operate in the scope of
      26                 :             :  * a single device. This usually means communicating with other devices, however
      27                 :             :  * plugins aren't required to be packet based.
      28                 :             :  *
      29                 :             :  * ## Plugin Requirements
      30                 :             :  *
      31                 :             :  * Device plugins essentially have two sets of dependent conditions for being
      32                 :             :  * enabled. Plugins become available (i.e. can be enabled) when any of the
      33                 :             :  * following are true:
      34                 :             :  *
      35                 :             :  * - any of the device's outgoing capabilities match any of the plugin's
      36                 :             :  *   incoming capabilities
      37                 :             :  * - any of the device's incoming capabilities match any of the plugin's
      38                 :             :  *   outgoing capabilities
      39                 :             :  * - the plugin doesn't list any capabilities (eg. a non-packet based plugin)
      40                 :             :  *
      41                 :             :  * When a plugin becomes available it may be enabled, disabled and configured.
      42                 :             :  *
      43                 :             :  * ## Plugin Actions
      44                 :             :  *
      45                 :             :  * `ValentDevicePlugin` implements the [iface@Gio.ActionGroup] and
      46                 :             :  * [iface@Gio.ActionMap] interfaces, providing a simple way for plugins to
      47                 :             :  * expose functions and states. Each [iface@Gio.Action] added to the action map
      48                 :             :  * will be included in the device action group with the plugin's module name as
      49                 :             :  * a prefix (eg. `share.uri`).
      50                 :             :  *
      51                 :             :  * If the [class@Valent.DeviceManager] is exported on D-Bus, the actions will be
      52                 :             :  * exported along with the [class@Valent.Device].
      53                 :             :  *
      54                 :             :  * ## Implementation Notes
      55                 :             :  *
      56                 :             :  * Implementations that define `X-DevicePluginIncoming` in the `.plugin` file
      57                 :             :  * must override [vfunc@Valent.DevicePlugin.handle_packet] to handle incoming
      58                 :             :  * packets. Implementations that depend on the device state, especially those
      59                 :             :  * that define `X-DevicePluginOutgoing` in the `.plugin` file, should override
      60                 :             :  * [vfunc@Valent.DevicePlugin.update_state].
      61                 :             :  *
      62                 :             :  * For device plugin preferences see [class@Valent.DevicePreferencesGroup].
      63                 :             :  *
      64                 :             :  * ## `.plugin` File
      65                 :             :  *
      66                 :             :  * Implementations may define the following extra fields in the `.plugin` file:
      67                 :             :  *
      68                 :             :  * - `X-DevicePluginCategory`
      69                 :             :  *
      70                 :             :  *     A list of categories separated by semi-colons, serving as a hint for
      71                 :             :  *     organizational purposes. This should be in the form `Main;Additional;`,
      72                 :             :  *     with values from the freedesktop.org Desktop Menu Specification.
      73                 :             :  *
      74                 :             :  * - `X-DevicePluginIncoming`
      75                 :             :  *
      76                 :             :  *     A list of packet types (eg. `kdeconnect.ping`) separated by semi-colons
      77                 :             :  *     indicating the packets that the plugin can handle.
      78                 :             :  *
      79                 :             :  * - `X-DevicePluginOutgoing`
      80                 :             :  *
      81                 :             :  *     A list of packet types (eg. `kdeconnect.share.request`) separated by
      82                 :             :  *     semi-colons indicating the packets that the plugin may send.
      83                 :             :  *
      84                 :             :  * - `X-DevicePluginSettings`
      85                 :             :  *
      86                 :             :  *     A [class@Gio.Settings] schema ID for the plugin's settings. See
      87                 :             :  *     [method@Valent.Context.get_plugin_settings] for more information.
      88                 :             :  *
      89                 :             :  * Since: 1.0
      90                 :             :  */
      91                 :             : 
      92                 :             : typedef struct
      93                 :             : {
      94                 :             :   gpointer  reserved[1];
      95                 :             : } ValentDevicePluginPrivate;
      96                 :             : 
      97   [ +  +  +  - ]:        4258 : G_DEFINE_ABSTRACT_TYPE (ValentDevicePlugin, valent_device_plugin, VALENT_TYPE_EXTENSION)
      98                 :             : 
      99                 :             : /**
     100                 :             :  * ValentDevicePluginClass:
     101                 :             :  * @handle_packet: the virtual function pointer for valent_device_plugin_handle_packet()
     102                 :             :  * @update_state: the virtual function pointer for valent_device_plugin_update_state()
     103                 :             :  *
     104                 :             :  * The virtual function table for `ValentDevicePlugin`.
     105                 :             :  */
     106                 :             : 
     107                 :             : 
     108                 :             : /* LCOV_EXCL_START */
     109                 :             : static void
     110                 :             : valent_device_plugin_real_handle_packet (ValentDevicePlugin *plugin,
     111                 :             :                                          const char         *type,
     112                 :             :                                          JsonNode           *packet)
     113                 :             : {
     114                 :             :   g_assert (VALENT_IS_DEVICE_PLUGIN (plugin));
     115                 :             :   g_assert (type != NULL && *type != '\0');
     116                 :             :   g_assert (VALENT_IS_PACKET (packet));
     117                 :             : 
     118                 :             :   g_critical ("%s: expected handler for \"%s\" packet",
     119                 :             :               G_OBJECT_TYPE_NAME (plugin),
     120                 :             :               type);
     121                 :             : }
     122                 :             : 
     123                 :             : static void
     124                 :             : valent_device_plugin_real_update_state (ValentDevicePlugin *plugin,
     125                 :             :                                         ValentDeviceState   state)
     126                 :             : {
     127                 :             :   g_assert (VALENT_IS_DEVICE_PLUGIN (plugin));
     128                 :             : }
     129                 :             : /* LCOV_EXCL_STOP */
     130                 :             : 
     131                 :             : static void
     132                 :         115 : valent_device_send_packet_cb (ValentDevice *device,
     133                 :             :                               GAsyncResult *result,
     134                 :             :                               gpointer      user_data)
     135                 :             : {
     136                 :         230 :   g_autoptr (GError) error = NULL;
     137                 :             : 
     138         [ +  + ]:         115 :   if (!valent_device_send_packet_finish (device, result, &error))
     139                 :             :     {
     140         [ -  + ]:           2 :       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
     141                 :           0 :         g_critical ("%s(): %s", G_STRFUNC, error->message);
     142         [ -  + ]:           2 :       else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED))
     143                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     144         [ +  - ]:           2 :       else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     145                 :           2 :         g_debug ("%s(): %s", G_STRFUNC, error->message);
     146                 :             :     }
     147                 :         115 : }
     148                 :             : 
     149                 :             : /*
     150                 :             :  * GObject
     151                 :             :  */
     152                 :             : static void
     153                 :          66 : valent_device_plugin_class_init (ValentDevicePluginClass *klass)
     154                 :             : {
     155                 :          66 :   klass->handle_packet = valent_device_plugin_real_handle_packet;
     156                 :          66 :   klass->update_state = valent_device_plugin_real_update_state;
     157                 :             : }
     158                 :             : 
     159                 :             : static void
     160                 :         199 : valent_device_plugin_init (ValentDevicePlugin *self)
     161                 :             : {
     162                 :         199 : }
     163                 :             : 
     164                 :             : /**
     165                 :             :  * valent_device_plugin_queue_packet:
     166                 :             :  * @plugin: a `ValentDevicePlugin`
     167                 :             :  * @packet: a KDE Connect packet
     168                 :             :  *
     169                 :             :  * Queue a KDE Connect packet to be sent to the device this plugin is bound to.
     170                 :             :  *
     171                 :             :  * For notification of success call [method@Valent.Extension.get_object] and
     172                 :             :  * then [method@Valent.Device.send_packet].
     173                 :             :  *
     174                 :             :  * Since: 1.0
     175                 :             :  */
     176                 :             : void
     177                 :         115 : valent_device_plugin_queue_packet (ValentDevicePlugin *plugin,
     178                 :             :                                    JsonNode           *packet)
     179                 :             : {
     180                 :         115 :   ValentDevice *device = NULL;
     181                 :         230 :   g_autoptr (GCancellable) destroy = NULL;
     182                 :             : 
     183         [ +  - ]:         115 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     184         [ -  + ]:         115 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     185                 :             : 
     186         [ +  - ]:         115 :   if ((device = valent_extension_get_object (VALENT_EXTENSION (plugin))) == NULL)
     187                 :             :     return;
     188                 :             : 
     189                 :         115 :   destroy = valent_object_ref_cancellable (VALENT_OBJECT (plugin));
     190         [ +  - ]:         115 :   valent_device_send_packet (device,
     191                 :             :                              packet,
     192                 :             :                              destroy,
     193                 :             :                              (GAsyncReadyCallback)valent_device_send_packet_cb,
     194                 :             :                              NULL);
     195                 :             : }
     196                 :             : 
     197                 :             : /**
     198                 :             :  * valent_device_plugin_show_notification:
     199                 :             :  * @plugin: a `ValentDevicePlugin`
     200                 :             :  * @id: an id for the notification
     201                 :             :  * @notification: a `GNotification`
     202                 :             :  *
     203                 :             :  * A convenience for showing a local notification.
     204                 :             :  *
     205                 :             :  * @id will be automatically prepended with the device ID and plugin module to
     206                 :             :  * prevent conflicting with other devices and plugins.
     207                 :             :  *
     208                 :             :  * Call [method@Valent.DevicePlugin.hide_notification] to make the same
     209                 :             :  * transformation on @id and withdraw the notification.
     210                 :             :  *
     211                 :             :  * Since: 1.0
     212                 :             :  */
     213                 :             : void
     214                 :          40 : valent_device_plugin_show_notification (ValentDevicePlugin *plugin,
     215                 :             :                                         const char         *id,
     216                 :             :                                         GNotification      *notification)
     217                 :             : {
     218                 :          40 :   GApplication *application = g_application_get_default ();
     219                 :          40 :   g_autoptr (ValentDevice) device = NULL;
     220   [ -  -  -  + ]:          40 :   g_autoptr (PeasPluginInfo) plugin_info = NULL;
     221   [ -  -  -  + ]:          40 :   g_autofree char *notification_id = NULL;
     222                 :             : 
     223         [ +  - ]:          40 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     224         [ -  + ]:          40 :   g_return_if_fail (id != NULL);
     225   [ +  -  +  -  :          40 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     226                 :             : 
     227         [ -  + ]:          40 :   if G_UNLIKELY (application == NULL)
     228                 :             :     return;
     229                 :             : 
     230                 :           0 :   g_object_get (plugin,
     231                 :             :                 "object",      &device,
     232                 :             :                 "plugin-info", &plugin_info,
     233                 :             :                 NULL);
     234                 :           0 :   notification_id = g_strdup_printf ("%s::%s::%s",
     235                 :             :                                      valent_device_get_id (device),
     236                 :             :                                      peas_plugin_info_get_module_name (plugin_info),
     237                 :             :                                      id);
     238                 :           0 :   g_application_send_notification (application, notification_id, notification);
     239                 :             : }
     240                 :             : 
     241                 :             : /**
     242                 :             :  * valent_device_plugin_hide_notification:
     243                 :             :  * @plugin: a `ValentDevicePlugin`
     244                 :             :  * @id: an id for the notification
     245                 :             :  *
     246                 :             :  * A convenience for withdrawing a notification.
     247                 :             :  *
     248                 :             :  * This method will withdraw a notification shown with
     249                 :             :  * [method@Valent.DevicePlugin.show_notification].
     250                 :             :  *
     251                 :             :  * Since: 1.0
     252                 :             :  */
     253                 :             : void
     254                 :          31 : valent_device_plugin_hide_notification (ValentDevicePlugin *plugin,
     255                 :             :                                         const char         *id)
     256                 :             : {
     257                 :          31 :   GApplication *application = g_application_get_default ();
     258                 :          31 :   g_autoptr (ValentDevice) device = NULL;
     259   [ -  -  -  + ]:          31 :   g_autoptr (PeasPluginInfo) plugin_info = NULL;
     260   [ -  -  -  + ]:          31 :   g_autofree char *notification_id = NULL;
     261                 :             : 
     262         [ +  - ]:          31 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     263         [ -  + ]:          31 :   g_return_if_fail (id != NULL);
     264                 :             : 
     265         [ -  + ]:          31 :   if G_UNLIKELY (application == NULL)
     266                 :             :     return;
     267                 :             : 
     268                 :           0 :   g_object_get (plugin,
     269                 :             :                 "object",      &device,
     270                 :             :                 "plugin-info", &plugin_info,
     271                 :             :                 NULL);
     272                 :           0 :   notification_id = g_strdup_printf ("%s::%s::%s",
     273                 :             :                                      valent_device_get_id (device),
     274                 :             :                                      peas_plugin_info_get_module_name (plugin_info),
     275                 :             :                                      id);
     276                 :           0 :   g_application_withdraw_notification (application, notification_id);
     277                 :             : }
     278                 :             : 
     279                 :             : /**
     280                 :             :  * valent_device_plugin_handle_packet: (virtual handle_packet)
     281                 :             :  * @plugin: a `ValentDevicePlugin`
     282                 :             :  * @type: a KDE Connect packet type
     283                 :             :  * @packet: a KDE Connect packet
     284                 :             :  *
     285                 :             :  * Handle a packet from the device the plugin is bound to.
     286                 :             :  *
     287                 :             :  * This is called when the device receives a packet type included in the
     288                 :             :  * `X-DevicePluginIncoming` field of the `.plugin` file.
     289                 :             :  *
     290                 :             :  * This is optional for implementations which do not register any incoming
     291                 :             :  * capabilities, such as plugins that do not provide packet-based functionality.
     292                 :             :  *
     293                 :             :  * Since: 1.0
     294                 :             :  */
     295                 :             : void
     296                 :         131 : valent_device_plugin_handle_packet (ValentDevicePlugin *plugin,
     297                 :             :                                     const char         *type,
     298                 :             :                                     JsonNode           *packet)
     299                 :             : {
     300                 :         131 :   VALENT_ENTRY;
     301                 :             : 
     302         [ +  - ]:         131 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     303   [ +  -  -  + ]:         131 :   g_return_if_fail (type != NULL && *type != '\0');
     304         [ -  + ]:         131 :   g_return_if_fail (VALENT_IS_PACKET (packet));
     305                 :             : 
     306                 :         131 :   VALENT_DEVICE_PLUGIN_GET_CLASS (plugin)->handle_packet (plugin, type, packet);
     307                 :             : 
     308                 :         131 :   VALENT_EXIT;
     309                 :             : }
     310                 :             : 
     311                 :             : /**
     312                 :             :  * valent_device_plugin_update_state: (virtual update_state)
     313                 :             :  * @plugin: a `ValentDevicePlugin`
     314                 :             :  * @state: a `ValentDeviceState`
     315                 :             :  *
     316                 :             :  * Update the plugin based on the new state of the device.
     317                 :             :  *
     318                 :             :  * This function is called when the connected or paired state of the device
     319                 :             :  * changes. This may be used to configure actions, event handlers that may
     320                 :             :  * trigger outgoing packets and exchange connect-time data with the device.
     321                 :             :  *
     322                 :             :  * This is optional for all implementations as plugins aren't required to be
     323                 :             :  * dependent on the device state.
     324                 :             :  *
     325                 :             :  * Since: 1.0
     326                 :             :  */
     327                 :             : void
     328                 :         596 : valent_device_plugin_update_state (ValentDevicePlugin *plugin,
     329                 :             :                                    ValentDeviceState   state)
     330                 :             : {
     331                 :         596 :   VALENT_ENTRY;
     332                 :             : 
     333         [ +  - ]:         596 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     334                 :             : 
     335                 :         596 :   VALENT_DEVICE_PLUGIN_GET_CLASS (plugin)->update_state (plugin, state);
     336                 :             : 
     337                 :         596 :   VALENT_EXIT;
     338                 :             : }
     339                 :             : 
     340                 :             : static int
     341                 :         449 : _g_menu_find_action (GMenuModel *menu,
     342                 :             :                      const char *action)
     343                 :             : {
     344                 :         449 :   int i, n_items;
     345                 :             : 
     346   [ +  -  +  -  :         449 :   g_assert (G_IS_MENU_MODEL (menu));
             +  -  -  + ]
     347         [ -  + ]:         449 :   g_assert (action != NULL);
     348                 :             : 
     349                 :         449 :   n_items = g_menu_model_get_n_items (menu);
     350                 :             : 
     351         [ +  + ]:        1048 :   for (i = 0; i < n_items; i++)
     352                 :             :     {
     353                 :         295 :       g_autofree char *item_str = NULL;
     354                 :             : 
     355                 :         295 :       g_menu_model_get_item_attribute (menu, i, "action", "s", &item_str);
     356                 :             : 
     357         [ +  + ]:         295 :       if (g_strcmp0 (item_str, action) == 0)
     358                 :         145 :         return i;
     359                 :             :     }
     360                 :             : 
     361                 :             :   return -1;
     362                 :             : }
     363                 :             : 
     364                 :             : /**
     365                 :             :  * valent_device_plugin_set_menu_action:
     366                 :             :  * @plugin: a `ValentDevicePlugin`
     367                 :             :  * @action: a `GAction` name
     368                 :             :  * @label: (nullable): a label for the action
     369                 :             :  * @icon_name: (nullable): an icon for the action
     370                 :             :  *
     371                 :             :  * Set or remove a device menu action by [iface@Gio.Action] name.
     372                 :             :  *
     373                 :             :  * If @label and @icon are %NULL, @action will be removed from the menu.
     374                 :             :  *
     375                 :             :  * Since: 1.0
     376                 :             :  */
     377                 :             : void
     378                 :         154 : valent_device_plugin_set_menu_action (ValentDevicePlugin *plugin,
     379                 :             :                                       const char         *action,
     380                 :             :                                       const char         *label,
     381                 :             :                                       const char         *icon_name)
     382                 :             : {
     383                 :         308 :   g_autoptr (GMenuItem) item = NULL;
     384                 :             : 
     385         [ +  - ]:         154 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     386   [ +  -  -  + ]:         154 :   g_return_if_fail (action != NULL && *action != '\0');
     387   [ +  -  +  -  :         154 :   g_return_if_fail ((label == NULL && icon_name == NULL) ||
                   -  + ]
     388                 :             :                     (label != NULL && *label != '\0'));
     389                 :             : 
     390         [ +  - ]:         154 :   if (label != NULL)
     391                 :             :     {
     392                 :         308 :       g_autoptr (GIcon) icon = NULL;
     393                 :             : 
     394         [ +  - ]:         154 :       if (icon_name != NULL)
     395                 :         154 :         icon = g_themed_icon_new (icon_name);
     396                 :             : 
     397                 :         154 :       item = g_menu_item_new (label, action);
     398                 :         154 :       g_menu_item_set_icon (item, icon);
     399         [ +  - ]:         154 :       g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
     400                 :             :     }
     401                 :             : 
     402         [ +  - ]:         154 :   valent_device_plugin_set_menu_item (plugin, action, item);
     403                 :             : }
     404                 :             : 
     405                 :             : /**
     406                 :             :  * valent_device_plugin_set_menu_item:
     407                 :             :  * @plugin: a `ValentDevicePlugin`
     408                 :             :  * @action: a `GAction` name
     409                 :             :  * @item: (nullable): a `GMenuItem`
     410                 :             :  *
     411                 :             :  * Set or remove a device [class@Gio.MenuItem] by [iface@Gio.Action] name.
     412                 :             :  *
     413                 :             :  * If @item is %NULL, @action will be removed from the menu.
     414                 :             :  *
     415                 :             :  * Since: 1.0
     416                 :             :  */
     417                 :             : void
     418                 :         451 : valent_device_plugin_set_menu_item (ValentDevicePlugin *plugin,
     419                 :             :                                     const char         *action,
     420                 :             :                                     GMenuItem          *item)
     421                 :             : {
     422                 :         451 :   ValentDevice *device = NULL;
     423                 :         451 :   GMenuModel *menu;
     424                 :         451 :   int index_ = -1;
     425                 :             : 
     426         [ +  - ]:         451 :   g_return_if_fail (VALENT_IS_DEVICE_PLUGIN (plugin));
     427   [ +  -  -  + ]:         451 :   g_return_if_fail (action != NULL && *action != '\0');
     428   [ +  +  +  -  :         451 :   g_return_if_fail (item == NULL || G_IS_MENU_ITEM (item));
             -  +  -  - ]
     429                 :             : 
     430                 :             :   /* NOTE: this method may be called by plugins in their `dispose()` */
     431         [ +  + ]:         451 :   if ((device = valent_extension_get_object (VALENT_EXTENSION (plugin))) == NULL)
     432                 :             :     return;
     433                 :             : 
     434                 :         449 :   menu = valent_device_get_menu (device);
     435                 :         449 :   index_ = _g_menu_find_action (menu, action);
     436                 :             : 
     437         [ +  + ]:         449 :   if (index_ > -1)
     438                 :         145 :     g_menu_remove (G_MENU (menu), index_);
     439                 :             : 
     440         [ +  + ]:         449 :   if (item != NULL)
     441                 :             :     {
     442         [ -  + ]:         155 :       if (index_ > -1)
     443                 :           0 :         g_menu_insert_item (G_MENU (menu), index_, item);
     444                 :             :       else
     445                 :         155 :         g_menu_append_item (G_MENU (menu), item);
     446                 :             :     }
     447                 :             : }
     448                 :             : 
     449                 :             : /**
     450                 :             :  * valent_notification_set_device_action:
     451                 :             :  * @notification: a `GNotification`
     452                 :             :  * @device: a `ValentDevice`
     453                 :             :  * @action: the device action name
     454                 :             :  * @target: (nullable): the action target
     455                 :             :  *
     456                 :             :  * Set the default action for @notification. @action is wrapped in the special
     457                 :             :  * `device` action for @device, which allows it to be activated from the `app`
     458                 :             :  * action scope.
     459                 :             :  *
     460                 :             :  * Since: 1.0
     461                 :             :  */
     462                 :             : void
     463                 :           1 : valent_notification_set_device_action (GNotification *notification,
     464                 :             :                                        ValentDevice  *device,
     465                 :             :                                        const char    *action,
     466                 :             :                                        GVariant      *target)
     467                 :             : {
     468                 :           1 :   GVariantBuilder builder;
     469                 :             : 
     470   [ +  -  +  -  :           1 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     471         [ -  + ]:           1 :   g_return_if_fail (VALENT_IS_DEVICE (device));
     472   [ +  -  -  + ]:           1 :   g_return_if_fail (action != NULL && *action != '\0');
     473                 :             : 
     474                 :           1 :   g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
     475                 :             : 
     476         [ +  - ]:           1 :   if (target != NULL)
     477                 :           1 :     g_variant_builder_add (&builder, "v", target);
     478                 :             : 
     479                 :           1 :   g_notification_set_default_action_and_target (notification,
     480                 :             :                                                 "app.device",
     481                 :             :                                                 "(ssav)",
     482                 :             :                                                 valent_device_get_id (device),
     483                 :             :                                                 action,
     484                 :             :                                                 &builder);
     485                 :             : }
     486                 :             : 
     487                 :             : /**
     488                 :             :  * valent_notification_add_device_button:
     489                 :             :  * @notification: a `GNotification`
     490                 :             :  * @device: a `ValentDevice`
     491                 :             :  * @label: the button label
     492                 :             :  * @action: the device action name
     493                 :             :  * @target: (nullable): the action target
     494                 :             :  *
     495                 :             :  * Add an action button to @notification. @action is wrapped in the special
     496                 :             :  * `device` action for @device, which allows it to be activated from the `app`
     497                 :             :  * action scope.
     498                 :             :  *
     499                 :             :  * Since: 1.0
     500                 :             :  */
     501                 :             : void
     502                 :          29 : valent_notification_add_device_button (GNotification *notification,
     503                 :             :                                        ValentDevice  *device,
     504                 :             :                                        const char    *label,
     505                 :             :                                        const char    *action,
     506                 :             :                                        GVariant      *target)
     507                 :             : {
     508                 :          29 :   GVariantBuilder builder;
     509                 :             : 
     510   [ +  -  +  -  :          29 :   g_return_if_fail (G_IS_NOTIFICATION (notification));
             -  +  -  - ]
     511         [ -  + ]:          29 :   g_return_if_fail (VALENT_IS_DEVICE (device));
     512   [ +  -  -  + ]:          29 :   g_return_if_fail (label != NULL && *label != '\0');
     513   [ +  -  -  + ]:          29 :   g_return_if_fail (action != NULL && *action != '\0');
     514                 :             : 
     515                 :          29 :   g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
     516                 :             : 
     517         [ +  + ]:          29 :   if (target != NULL)
     518                 :          24 :     g_variant_builder_add (&builder, "v", target);
     519                 :             : 
     520                 :          29 :   g_notification_add_button_with_target (notification,
     521                 :             :                                          label,
     522                 :             :                                          "app.device",
     523                 :             :                                          "(ssav)",
     524                 :             :                                          valent_device_get_id (device),
     525                 :             :                                          action,
     526                 :             :                                          &builder);
     527                 :             : }
     528                 :             : 
        

Generated by: LCOV version 2.0-1