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-upload"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <json-glib/json-glib.h>
10 : : #include <valent.h>
11 : :
12 : : #ifdef HAVE_GLYCIN
13 : : #include <glycin.h>
14 : : #endif /* HAVE_GLYCIN */
15 : : #ifdef HAVE_GTK4
16 : : #include <gtk/gtk.h>
17 : : #endif /* HAVE_GTK4 */
18 : :
19 : : #include "valent-notification-upload.h"
20 : :
21 : : #define DEFAULT_ICON_SIZE 512
22 : :
23 : :
24 : : /**
25 : : * ValentNotificationUpload:
26 : : *
27 : : * A class for notification icon uploads.
28 : : *
29 : : * `ValentNotificationUpload` is a class that abstracts uploading notifications
30 : : * with icon payloads for `ValentNotificationPlugin`.
31 : : */
32 : :
33 : : struct _ValentNotificationUpload
34 : : {
35 : : ValentTransfer parent_instance;
36 : :
37 : : ValentDevice *device;
38 : : JsonNode *packet;
39 : : GIcon *icon;
40 : : };
41 : :
42 [ + + + - ]: 10 : G_DEFINE_FINAL_TYPE (ValentNotificationUpload, valent_notification_upload, VALENT_TYPE_TRANSFER)
43 : :
44 : : typedef enum {
45 : : PROP_DEVICE = 1,
46 : : PROP_ICON,
47 : : PROP_PACKET,
48 : : } ValentNotificationUploadProperty;
49 : :
50 : : static GParamSpec *properties[PROP_PACKET + 1] = { NULL, };
51 : :
52 : : static ValentChannel *
53 : 0 : _valent_device_next_channel (ValentDevice *device,
54 : : ValentChannel *channel)
55 : : {
56 : 0 : GListModel *channels = NULL;
57 : 0 : unsigned int n_items = 0;
58 : :
59 : 0 : channels = valent_device_get_channels (device);
60 : 0 : n_items = g_list_model_get_n_items (channels);
61 [ # # ]: 0 : for (unsigned int i = 0; i < n_items; i++)
62 : : {
63 : 0 : g_autoptr (ValentChannel) next = NULL;
64 : :
65 : 0 : next = g_list_model_get_item (channels, i);
66 [ # # ]: 0 : if (next != channel)
67 : : return g_steal_pointer (&next);
68 : : }
69 : :
70 : : return NULL;
71 : : }
72 : :
73 : : /*
74 : : * GtkIconTheme Helpers
75 : : */
76 : : #ifdef HAVE_GTK4
77 : : static GtkIconTheme *
78 : 2 : _gtk_icon_theme_get_default (void)
79 : : {
80 : 2 : static GtkIconTheme *icon_theme = NULL;
81 : 2 : size_t guard = 0;
82 : :
83 [ + - + - ]: 2 : if (g_once_init_enter (&guard))
84 : : {
85 [ + - ]: 2 : if (gtk_is_initialized ())
86 : : {
87 : 2 : GdkDisplay *display = NULL;
88 : :
89 : 2 : display = gdk_display_get_default ();
90 [ + - ]: 2 : if (display != NULL)
91 : 2 : icon_theme = gtk_icon_theme_get_for_display (display);
92 : : }
93 : :
94 : 2 : g_once_init_leave (&guard, 1);
95 : : }
96 : :
97 : 2 : return icon_theme;
98 : : }
99 : :
100 : : static int
101 : 2 : _gtk_icon_theme_get_largest_size (GtkIconTheme *theme,
102 : : const char *name)
103 : : {
104 : 4 : g_autofree int *sizes = NULL;
105 : 2 : int ret = 0;
106 : :
107 [ + - + - : 2 : g_assert (GTK_IS_ICON_THEME (theme));
- + - - ]
108 [ + - ]: 2 : g_assert (name != NULL);
109 : :
110 [ + - ]: 2 : if (!gtk_icon_theme_has_icon (theme, name))
111 : : return ret;
112 : :
113 : 2 : sizes = gtk_icon_theme_get_icon_sizes (theme, name);
114 [ + - ]: 2 : for (unsigned int i = 0; sizes[i] != 0; i++)
115 : : {
116 [ - + ]: 2 : if (sizes[i] == -1)
117 : : return -1;
118 : :
119 : 0 : if (sizes[i] > ret)
120 : : ret = sizes[i];
121 : : }
122 : :
123 : : return ret;
124 : : }
125 : :
126 : : static GFile *
127 : 2 : _gtk_icon_theme_get_largest_file (GIcon *icon,
128 : : GError **error)
129 : : {
130 : 2 : GtkIconTheme *icon_theme = NULL;
131 : 2 : const char * const *names;
132 : :
133 [ + - + - : 2 : g_assert (G_IS_THEMED_ICON (icon));
- + - - ]
134 : :
135 : 2 : icon_theme = _gtk_icon_theme_get_default ();
136 [ - + ]: 2 : if (icon_theme == NULL)
137 : : {
138 : 0 : g_set_error_literal (error,
139 : : G_IO_ERROR,
140 : : G_IO_ERROR_NOT_SUPPORTED,
141 : : "Failed to load icon theme");
142 : 0 : return NULL;
143 : : }
144 : :
145 : 2 : names = g_themed_icon_get_names (G_THEMED_ICON (icon));
146 [ + - ]: 2 : for (size_t i = 0; names[i]; i++)
147 : : {
148 : 2 : g_autoptr (GtkIconPaintable) paintable = NULL;
149 : 2 : int size;
150 : :
151 : 2 : size = _gtk_icon_theme_get_largest_size (icon_theme, names[i]);
152 [ - + ]: 2 : if (size == 0)
153 : 0 : continue;
154 : :
155 : 2 : paintable = gtk_icon_theme_lookup_icon (icon_theme,
156 : : names[i],
157 : : NULL,
158 : : size,
159 : : 1,
160 : : GTK_TEXT_DIR_NONE,
161 : : 0);
162 [ + - ]: 2 : if (paintable != NULL)
163 : : {
164 : 2 : g_autoptr (GFile) ret = NULL;
165 : :
166 : 2 : ret = gtk_icon_paintable_get_file (paintable);
167 [ + - ]: 2 : if (ret != NULL)
168 : 2 : return g_steal_pointer (&ret);
169 : : }
170 : : }
171 : :
172 : 0 : g_set_error_literal (error,
173 : : G_IO_ERROR,
174 : : G_IO_ERROR_NOT_FOUND,
175 : : "Failed to find icon");
176 : 0 : return NULL;
177 : : }
178 : : #endif /* HAVE_GTK4 */
179 : :
180 : : /*
181 : : * Glycin Helpers
182 : : */
183 : : #ifdef HAVE_GLYCIN
184 : : static void
185 : 4 : _glycin_encode_icon_bytes_task (GTask *task,
186 : : gpointer source_object,
187 : : gpointer task_data,
188 : : GCancellable *cancellable)
189 : : {
190 : 4 : GBytes *bytes = (GBytes *)task_data;
191 : 4 : g_autoptr (GlyLoader) loader = NULL;
192 [ + - - - ]: 4 : g_autoptr (GlyImage) image = NULL;
193 : 4 : const char *mime_type = NULL;
194 : 4 : GError *error = NULL;
195 : :
196 : : /* Attempt to load the image data
197 : : */
198 : 4 : loader = gly_loader_new_for_bytes (bytes);
199 : 4 : image = gly_loader_load (loader, &error);
200 [ - + ]: 4 : if (image == NULL)
201 : : {
202 : 0 : g_task_return_error (task, g_steal_pointer (&error));
203 : 0 : return;
204 : : }
205 : :
206 : : /* Attempt to convert the image data
207 : : *
208 : : * NOTE: kdeconnect-android only seems to accept PNG/JPEG
209 : : */
210 : 4 : mime_type = gly_image_get_mime_type (image);
211 [ + - + + ]: 8 : if (g_strcmp0 (mime_type, "image/jpeg") != 0 &&
212 : 4 : g_strcmp0 (mime_type, "image/png") != 0)
213 : : {
214 [ - - ]: 4 : g_autoptr (GlyFrameRequest) request = NULL;
215 [ + - - - ]: 2 : g_autoptr (GlyFrame) frame = NULL;
216 [ - - ]: 2 : g_autoptr (GlyCreator) creator = NULL;
217 [ - - ]: 2 : g_autoptr (GlyNewFrame) new_frame = NULL;
218 [ - - ]: 2 : g_autoptr (GlyEncodedImage) encoded_image = NULL;
219 : :
220 : : /* Attempt to resize the icon
221 : : */
222 : 2 : request = gly_frame_request_new ();
223 : 2 : gly_frame_request_set_scale (request, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE);
224 : 2 : frame = gly_image_get_specific_frame (image, request, &error);
225 [ - + ]: 2 : if (frame == NULL)
226 : : {
227 : 0 : g_task_return_error (task, g_steal_pointer (&error));
228 : 0 : return;
229 : : }
230 : :
231 : 2 : creator = gly_creator_new ("image/png", &error);
232 [ - + ]: 2 : if (creator == NULL)
233 : : {
234 : 0 : g_task_return_error (task, g_steal_pointer (&error));
235 : 0 : return;
236 : : }
237 : :
238 : 2 : new_frame = gly_creator_add_frame_with_stride (creator,
239 : : gly_frame_get_width (frame),
240 : : gly_frame_get_height (frame),
241 : : gly_frame_get_stride (frame),
242 : : gly_frame_get_memory_format (frame),
243 : : gly_frame_get_buf_bytes (frame),
244 : : &error);
245 [ - + ]: 2 : if (new_frame == NULL)
246 : : {
247 : 0 : g_task_return_error (task, g_steal_pointer (&error));
248 : 0 : return;
249 : : }
250 : :
251 : 2 : encoded_image = gly_creator_create (creator, &error);
252 [ - + ]: 2 : if (encoded_image == NULL)
253 : : {
254 : 0 : g_task_return_error (task, g_steal_pointer (&error));
255 : 0 : return;
256 : : }
257 : :
258 : 2 : g_task_return_pointer (task,
259 : 2 : gly_encoded_image_get_data (encoded_image),
260 : : (GDestroyNotify)g_bytes_unref);
261 : : }
262 : : else
263 : : {
264 : 2 : g_task_return_pointer (task,
265 : 2 : g_bytes_ref (bytes),
266 : : (GDestroyNotify)g_bytes_unref);
267 : : }
268 : : }
269 : : #endif /* HAVE_GLYCIN */
270 : :
271 : : static void
272 : 4 : _glycin_encode_icon_bytes (GBytes *bytes,
273 : : GCancellable *cancellable,
274 : : GAsyncReadyCallback callback,
275 : : gpointer user_data)
276 : : {
277 : 8 : g_autoptr (GTask) task = NULL;
278 : :
279 [ - + ]: 4 : g_assert (bytes != NULL);
280 [ + - + - : 4 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
281 : :
282 : 4 : task = g_task_new (NULL, cancellable, callback, user_data);
283 [ + - ]: 4 : g_task_set_source_tag (task, _glycin_encode_icon_bytes);
284 : : #ifdef HAVE_GLYCIN
285 : 4 : g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
286 [ + - ]: 4 : g_task_run_in_thread (task, _glycin_encode_icon_bytes_task);
287 : : #else
288 : : g_task_return_pointer (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
289 : : #endif /* HAVE_GLYCIN */
290 : 4 : }
291 : :
292 : : static GBytes *
293 : 4 : _glycin_encode_icon_bytes_finish (GAsyncResult *result,
294 : : GError **error)
295 : : {
296 [ + - + - : 4 : g_assert (G_IS_TASK (result));
- + - - ]
297 [ + - + - ]: 4 : g_assert (error == NULL || *error == NULL);
298 : :
299 : 4 : return g_task_propagate_pointer (G_TASK (result), error);
300 : : }
301 : :
302 : : /*
303 : : * ValentTransfer Helpers
304 : : */
305 : : static void
306 : 4 : g_output_stream_write_all_cb (GOutputStream *stream,
307 : : GAsyncResult *result,
308 : : gpointer user_data)
309 : : {
310 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
311 : 4 : GError *error = NULL;
312 : :
313 [ - + ]: 4 : if (!g_output_stream_write_all_finish (stream, result, NULL, &error))
314 : : {
315 : 0 : g_task_return_error (task, g_steal_pointer (&error));
316 [ # # ]: 0 : return;
317 : : }
318 : :
319 [ + - ]: 4 : g_task_return_boolean (task, TRUE);
320 : : }
321 : :
322 : : static void
323 : 4 : valent_channel_upload_cb (ValentChannel *channel,
324 : : GAsyncResult *result,
325 : : gpointer user_data)
326 : : {
327 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
328 : 4 : ValentNotificationUpload *self = g_task_get_source_object (task);
329 : 4 : GCancellable *cancellable = g_task_get_cancellable (task);
330 [ + - - - ]: 4 : g_autoptr (GIOStream) target = NULL;
331 : 4 : GBytes *bytes = NULL;
332 : 4 : const uint8_t *payload_data = NULL;
333 : 4 : size_t payload_size = 0;
334 : 4 : GError *error = NULL;
335 : :
336 : 4 : target = valent_channel_upload_finish (channel, result, &error);
337 [ - + ]: 4 : if (target == NULL)
338 : : {
339 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
340 : : {
341 : 0 : g_autoptr (ValentChannel) next = NULL;
342 : :
343 : 0 : next = _valent_device_next_channel (self->device, channel);
344 [ # # ]: 0 : if (next != NULL)
345 : : {
346 : 0 : valent_channel_upload_async (next,
347 : : self->packet,
348 : : g_task_get_cancellable (task),
349 : : (GAsyncReadyCallback)valent_channel_upload_cb,
350 : : g_object_ref (task));
351 : 0 : return;
352 : : }
353 : : }
354 : :
355 : 0 : g_task_return_error (task, g_steal_pointer (&error));
356 : 0 : return;
357 : : }
358 : :
359 : 4 : bytes = g_task_get_task_data (task);
360 : 4 : payload_data = g_bytes_get_data (bytes, &payload_size);
361 : 4 : g_output_stream_write_all_async (g_io_stream_get_output_stream (target),
362 : : payload_data,
363 : : payload_size,
364 : : G_PRIORITY_DEFAULT,
365 : : cancellable,
366 : : (GAsyncReadyCallback)g_output_stream_write_all_cb,
367 : : g_object_ref (task));
368 : : }
369 : :
370 : : static void
371 : 4 : valent_notification_upload_transfer_bytes (GTask *task,
372 : : GBytes *bytes)
373 : : {
374 : 4 : ValentNotificationUpload *self = g_task_get_source_object (task);
375 : 4 : GCancellable *cancellable = g_task_get_cancellable (task);
376 : 4 : g_autoptr (ValentChannel) channel = NULL;
377 : 4 : g_autofree char *payload_hash = NULL;
378 : 4 : const uint8_t *payload_data = NULL;
379 : 4 : size_t payload_size = 0;
380 : :
381 : : /* A payload hash is included, allowing the remote device to ignore icon
382 : : * transfers that it already has cached.
383 : : */
384 : 4 : payload_data = g_bytes_get_data (bytes, &payload_size);
385 : 4 : payload_hash = g_compute_checksum_for_data (G_CHECKSUM_MD5,
386 : : payload_data,
387 : : payload_size);
388 : 4 : json_object_set_string_member (valent_packet_get_body (self->packet),
389 : : "payloadHash",
390 : : payload_hash);
391 : 4 : valent_packet_set_payload_size (self->packet, payload_size);
392 : :
393 : 4 : channel = g_list_model_get_item (valent_device_get_channels (self->device), 0);
394 [ - + ]: 4 : if (channel == NULL)
395 : : {
396 : 0 : g_task_return_new_error (task,
397 : : G_IO_ERROR,
398 : : G_IO_ERROR_NOT_CONNECTED,
399 : : "Device is disconnected");
400 : 0 : return;
401 : : }
402 : :
403 : 4 : g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
404 : 4 : valent_channel_upload_async (channel,
405 : : self->packet,
406 : : cancellable,
407 : : (GAsyncReadyCallback)valent_channel_upload_cb,
408 : : g_object_ref (task));
409 : : }
410 : :
411 : : static void
412 : 4 : _glycin_encode_icon_bytes_cb (GObject *object,
413 : : GAsyncResult *result,
414 : : gpointer user_data)
415 : : {
416 : 4 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
417 [ + - ]: 4 : g_autoptr (GBytes) bytes = NULL;
418 : 4 : GError *error = NULL;
419 : :
420 : 4 : bytes = _glycin_encode_icon_bytes_finish (result, &error);
421 [ - + ]: 4 : if (bytes == NULL)
422 : : {
423 : 0 : g_debug ("%s(): %s", G_STRFUNC, error->message);
424 : 0 : bytes = g_bytes_ref (g_task_get_task_data (task));
425 : : }
426 : :
427 [ + - ]: 4 : valent_notification_upload_transfer_bytes (task, bytes);
428 : 4 : }
429 : :
430 : : static void
431 : 3 : g_file_load_bytes_cb (GFile *file,
432 : : GAsyncResult *result,
433 : : gpointer user_data)
434 : : {
435 : 6 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
436 : 3 : GCancellable *cancellable = g_task_get_cancellable (task);
437 [ - - + - ]: 3 : g_autoptr (GBytes) bytes = NULL;
438 : 3 : GError *error = NULL;
439 : :
440 : 3 : bytes = g_file_load_bytes_finish (file, result, NULL, &error);
441 [ - + ]: 3 : if (bytes == NULL)
442 : : {
443 : 0 : g_task_return_error (task, g_steal_pointer (&error));
444 [ # # ]: 0 : return;
445 : : }
446 : :
447 : 3 : g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
448 : 3 : _glycin_encode_icon_bytes (bytes,
449 : : cancellable,
450 : : (GAsyncReadyCallback)_glycin_encode_icon_bytes_cb,
451 : : g_object_ref (task));
452 : : }
453 : :
454 : : static void
455 : 4 : valent_notification_upload_transfer_icon (GTask *task,
456 : : GIcon *icon)
457 : : {
458 : 4 : GCancellable *cancellable = g_task_get_cancellable (task);
459 : 4 : GError *error = NULL;
460 : :
461 [ + - + - : 4 : g_assert (G_IS_TASK (task));
- + - - ]
462 [ + - + - : 4 : g_assert (G_IS_ICON (icon));
+ - + - ]
463 : :
464 [ + - + + : 4 : if (G_IS_BYTES_ICON (icon))
- + ]
465 : : {
466 : 1 : GBytes *bytes;
467 : :
468 : 1 : bytes = g_bytes_icon_get_bytes (G_BYTES_ICON (icon));
469 : 1 : g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
470 : 1 : _glycin_encode_icon_bytes (bytes,
471 : : cancellable,
472 : : (GAsyncReadyCallback)_glycin_encode_icon_bytes_cb,
473 : : g_object_ref (task));
474 : : }
475 [ + - + + : 3 : else if (G_IS_FILE_ICON (icon))
- + ]
476 : : {
477 : 1 : GFile *file;
478 : :
479 : 1 : file = g_file_icon_get_file (G_FILE_ICON (icon));
480 : 1 : g_file_load_bytes_async (file,
481 : : cancellable,
482 : : (GAsyncReadyCallback)g_file_load_bytes_cb,
483 : : g_object_ref (task));
484 : : }
485 : : #ifdef HAVE_GTK4
486 [ + - - + : 2 : else if (G_IS_THEMED_ICON (icon))
- - ]
487 : : {
488 : 2 : g_autoptr (GFile) file = NULL;
489 : :
490 : 2 : file = _gtk_icon_theme_get_largest_file (icon, &error);
491 [ - + ]: 2 : if (file == NULL)
492 : : {
493 : 0 : g_task_return_error (task, g_steal_pointer (&error));
494 : 0 : return;
495 : : }
496 : :
497 : 2 : g_file_load_bytes_async (file,
498 : : cancellable,
499 : : (GAsyncReadyCallback)g_file_load_bytes_cb,
500 : : g_object_ref (task));
501 : : }
502 : : #endif /* HAVE_GTK4 */
503 : : else
504 : : {
505 : 0 : g_task_return_new_error (task,
506 : : G_IO_ERROR,
507 : : G_IO_ERROR_NOT_SUPPORTED,
508 : : "Unsupported icon type \"%s\"",
509 : : G_OBJECT_TYPE_NAME (icon));
510 : : }
511 : : }
512 : :
513 : : /*
514 : : * ValentTransfer
515 : : */
516 : : static void
517 : 4 : valent_notification_upload_execute (ValentTransfer *transfer,
518 : : GCancellable *cancellable,
519 : : GAsyncReadyCallback callback,
520 : : gpointer user_data)
521 : : {
522 : 4 : ValentNotificationUpload *self = VALENT_NOTIFICATION_UPLOAD (transfer);
523 : 8 : g_autoptr (GTask) task = NULL;
524 : :
525 [ - + ]: 4 : g_assert (VALENT_IS_NOTIFICATION_UPLOAD (self));
526 [ + - + - : 4 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
527 : :
528 : 4 : task = g_task_new (transfer, cancellable, callback, user_data);
529 [ + - ]: 4 : g_task_set_source_tag (task, valent_notification_upload_execute);
530 [ + - ]: 4 : valent_notification_upload_transfer_icon (task, self->icon);
531 : 4 : }
532 : :
533 : : /*
534 : : * GObject
535 : : */
536 : : static void
537 : 4 : valent_notification_upload_finalize (GObject *object)
538 : : {
539 : 4 : ValentNotificationUpload *self = VALENT_NOTIFICATION_UPLOAD (object);
540 : :
541 : 4 : valent_object_lock (VALENT_OBJECT (self));
542 [ + - ]: 4 : g_clear_object (&self->device);
543 [ + - ]: 4 : g_clear_object (&self->icon);
544 [ + - ]: 4 : g_clear_pointer (&self->packet, json_node_unref);
545 : 4 : valent_object_unlock (VALENT_OBJECT (self));
546 : :
547 : 4 : G_OBJECT_CLASS (valent_notification_upload_parent_class)->finalize (object);
548 : 4 : }
549 : :
550 : : static void
551 : 0 : valent_notification_upload_get_property (GObject *object,
552 : : guint prop_id,
553 : : GValue *value,
554 : : GParamSpec *pspec)
555 : : {
556 : 0 : ValentNotificationUpload *self = VALENT_NOTIFICATION_UPLOAD (object);
557 : :
558 [ # # # # ]: 0 : switch ((ValentNotificationUploadProperty)prop_id)
559 : : {
560 : : case PROP_DEVICE:
561 : 0 : valent_object_lock (VALENT_OBJECT (self));
562 : 0 : g_value_set_object (value, self->device);
563 : 0 : valent_object_unlock (VALENT_OBJECT (self));
564 : 0 : break;
565 : :
566 : : case PROP_ICON:
567 : 0 : valent_object_lock (VALENT_OBJECT (self));
568 : 0 : g_value_set_object (value, self->icon);
569 : 0 : valent_object_unlock (VALENT_OBJECT (self));
570 : 0 : break;
571 : :
572 : : case PROP_PACKET:
573 : 0 : valent_object_lock (VALENT_OBJECT (self));
574 : 0 : g_value_set_boxed (value, self->packet);
575 : 0 : valent_object_unlock (VALENT_OBJECT (self));
576 : 0 : break;
577 : :
578 : 0 : default:
579 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
580 : : }
581 : 0 : }
582 : :
583 : : static void
584 : 12 : valent_notification_upload_set_property (GObject *object,
585 : : guint prop_id,
586 : : const GValue *value,
587 : : GParamSpec *pspec)
588 : : {
589 : 12 : ValentNotificationUpload *self = VALENT_NOTIFICATION_UPLOAD (object);
590 : :
591 [ + + + - ]: 12 : switch ((ValentNotificationUploadProperty)prop_id)
592 : : {
593 : : case PROP_DEVICE:
594 : 4 : valent_object_lock (VALENT_OBJECT (self));
595 : 4 : self->device = g_value_dup_object (value);
596 : 4 : valent_object_unlock (VALENT_OBJECT (self));
597 : 4 : break;
598 : :
599 : : case PROP_ICON:
600 : 4 : valent_object_lock (VALENT_OBJECT (self));
601 : 4 : self->icon = g_value_dup_object (value);
602 : 4 : valent_object_unlock (VALENT_OBJECT (self));
603 : 4 : break;
604 : :
605 : : case PROP_PACKET:
606 : 4 : valent_object_lock (VALENT_OBJECT (self));
607 : 4 : self->packet = g_value_dup_boxed (value);
608 : 4 : valent_object_unlock (VALENT_OBJECT (self));
609 : 4 : break;
610 : :
611 : 0 : default:
612 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
613 : : }
614 : 12 : }
615 : :
616 : : static void
617 : 1 : valent_notification_upload_class_init (ValentNotificationUploadClass *klass)
618 : : {
619 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
620 : 1 : ValentTransferClass *transfer_class = VALENT_TRANSFER_CLASS (klass);
621 : :
622 : 1 : object_class->finalize = valent_notification_upload_finalize;
623 : 1 : object_class->get_property = valent_notification_upload_get_property;
624 : 1 : object_class->set_property = valent_notification_upload_set_property;
625 : :
626 : 1 : transfer_class->execute = valent_notification_upload_execute;
627 : :
628 : : /**
629 : : * ValentNotificationUpload:device:
630 : : *
631 : : * The [class@Valent.Device] this transfer is for.
632 : : */
633 : 2 : properties [PROP_DEVICE] =
634 : 1 : g_param_spec_object ("device", NULL, NULL,
635 : : VALENT_TYPE_DEVICE,
636 : : (G_PARAM_READWRITE |
637 : : G_PARAM_CONSTRUCT_ONLY |
638 : : G_PARAM_EXPLICIT_NOTIFY |
639 : : G_PARAM_STATIC_STRINGS));
640 : :
641 : : /**
642 : : * ValentNotificationUpload:icon:
643 : : *
644 : : * The [iface@Gio.Icon] for the notification.
645 : : */
646 : 2 : properties [PROP_ICON] =
647 : 1 : g_param_spec_object ("icon", NULL, NULL,
648 : : G_TYPE_ICON,
649 : : (G_PARAM_READWRITE |
650 : : G_PARAM_CONSTRUCT_ONLY |
651 : : G_PARAM_EXPLICIT_NOTIFY |
652 : : G_PARAM_STATIC_STRINGS));
653 : :
654 : : /**
655 : : * ValentNotificationUpload:packet:
656 : : *
657 : : * The packet to send the payload with.
658 : : */
659 : 2 : properties [PROP_PACKET] =
660 : 1 : g_param_spec_boxed ("packet", NULL, NULL,
661 : : JSON_TYPE_NODE,
662 : : (G_PARAM_READWRITE |
663 : : G_PARAM_CONSTRUCT_ONLY |
664 : : G_PARAM_EXPLICIT_NOTIFY |
665 : : G_PARAM_STATIC_STRINGS));
666 : :
667 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
668 : 1 : }
669 : :
670 : : static void
671 : 4 : valent_notification_upload_init (ValentNotificationUpload *self)
672 : : {
673 : 4 : }
674 : :
675 : : /**
676 : : * valent_notification_upload_new:
677 : : * @device: a `ValentDevice`
678 : : * @packet: a KDE Connect packet
679 : : * @icon: a `GIcon`
680 : : *
681 : : * Create a new `ValentNotificationUpload` for @packet and @icon.
682 : : *
683 : : * Returns: (transfer full): a `ValentNotificationUpload`
684 : : */
685 : : ValentTransfer *
686 : 4 : valent_notification_upload_new (ValentDevice *device,
687 : : JsonNode *packet,
688 : : GIcon *icon)
689 : : {
690 [ - + ]: 4 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
691 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
692 [ + - + - : 4 : g_return_val_if_fail (G_IS_ICON (icon), NULL);
+ - + - ]
693 : :
694 : 4 : return g_object_new (VALENT_TYPE_NOTIFICATION_UPLOAD,
695 : : "device", device,
696 : : "icon", icon,
697 : : "packet", packet,
698 : : NULL);
699 : : }
700 : :
|