LCOV - code coverage report
Current view: top level - src/libvalent/device - valent-device-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 84.6 % 117 99
Test Date: 2024-11-22 21:57:18 Functions: 93.3 % 15 14
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 46.2 % 158 73

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

Generated by: LCOV version 2.0-1