GCC Code Coverage Report


Directory: ./
File: panels/wwan/cc-wwan-device-page.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 257 0.0%
Functions: 0 25 0.0%
Branches: 0 119 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* cc-wwan-device-page.c
3 *
4 * Copyright 2019 Purism SPC
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author(s):
20 * Mohammed Sadiq <sadiq@sadiqpk.org>
21 *
22 * SPDX-License-Identifier: GPL-3.0-or-later
23 */
24
25 #undef G_LOG_DOMAIN
26 #define G_LOG_DOMAIN "cc-wwan-device-page"
27
28 #include <config.h>
29 #include <glib/gi18n.h>
30 #include <libmm-glib.h>
31 #define GCR_API_SUBJECT_TO_CHANGE
32 #include <gcr/gcr.h>
33
34 #include "cc-list-row.h"
35 #include "cc-wwan-data.h"
36 #include "cc-wwan-mode-dialog.h"
37 #include "cc-wwan-network-dialog.h"
38 #include "cc-wwan-details-dialog.h"
39 #include "cc-wwan-sim-lock-dialog.h"
40 #include "cc-wwan-apn-dialog.h"
41 #include "cc-wwan-device-page.h"
42 #include "cc-wwan-resources.h"
43
44 #include "shell/cc-application.h"
45 #include "shell/cc-object-storage.h"
46
47 /**
48 * @short_description: Device settings page
49 * @include: "cc-wwan-device-page.h"
50 *
51 * The Device page allows users to configure device
52 * settings. Please note that there is no one-to-one
53 * maping for a device settings page and a physical
54 * device. Say, if a device have two SIM card slots,
55 * there should be two device pages, one for each SIM.
56 */
57
58 struct _CcWwanDevicePage
59 {
60 GtkBox parent_instance;
61
62 GtkListBox *advanced_settings_list;
63 CcListRow *apn_settings_row;
64 AdwSwitchRow *data_enable_row;
65 AdwSwitchRow *data_roaming_row;
66 GtkListBox *data_settings_list;
67 CcListRow *details_row;
68 GtkStack *main_stack;
69 CcListRow *network_mode_row;
70 CcListRow *network_name_row;
71 GtkListBox *network_settings_list;
72 CcListRow *sim_lock_row;
73 GtkButton *unlock_button;
74
75 AdwToastOverlay *toast_overlay;
76
77 CcWwanDevice *device;
78 CcWwanData *wwan_data;
79 GDBusProxy *wwan_proxy;
80
81 GtkWindow *apn_dialog;
82 GtkWindow *details_dialog;
83 GtkWindow *network_mode_dialog;
84 GtkWindow *network_dialog;
85 GtkWindow *sim_lock_dialog;
86
87 gint sim_index;
88 /* Set if a change is triggered in a signal’s callback,
89 * to avoid re-triggering of callback. This is used
90 * instead of blocking handlers where the signal may be
91 * emitted async and the block/unblock may not work right
92 */
93 gboolean is_self_change;
94 gboolean is_retry;
95 };
96
97 G_DEFINE_TYPE (CcWwanDevicePage, cc_wwan_device_page, GTK_TYPE_BOX)
98
99 enum {
100 PROP_0,
101 PROP_DEVICE,
102 N_PROPS
103 };
104
105 static GParamSpec *properties[N_PROPS];
106
107 static void
108 wwan_device_page_handle_data_row (CcWwanDevicePage *self,
109 AdwSwitchRow *data_row)
110 {
111 gboolean active;
112
113 /* The user dismissed the dialog for selecting default APN */
114 if (cc_wwan_data_get_default_apn (self->wwan_data) == NULL)
115 {
116 self->is_self_change = TRUE;
117 gtk_widget_activate (GTK_WIDGET (data_row));
118
119 return;
120 }
121
122 active = adw_switch_row_get_active (data_row);
123
124 if (data_row == self->data_enable_row)
125 cc_wwan_data_set_enabled (self->wwan_data, active);
126 else
127 cc_wwan_data_set_roaming_enabled (self->wwan_data, active);
128
129 cc_wwan_data_save_settings (self->wwan_data, NULL, NULL, NULL);
130 }
131
132 static gboolean
133 wwan_apn_dialog_closed_cb (CcWwanDevicePage *self)
134 {
135 AdwSwitchRow *data_row;
136
137 if (gtk_widget_in_destruction (GTK_WIDGET (self)))
138 return FALSE;
139
140 data_row = g_object_get_data (G_OBJECT (self->apn_dialog), "row");
141 g_object_set_data (G_OBJECT (self->apn_dialog), "row", NULL);
142
143 if (data_row)
144 wwan_device_page_handle_data_row (self, data_row);
145
146 return FALSE;
147 }
148
149 static void
150 wwan_data_show_apn_dialog (CcWwanDevicePage *self)
151 {
152 GtkWindow *top_level;
153
154 top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
155
156 if (!self->apn_dialog)
157 {
158 self->apn_dialog = cc_wwan_apn_dialog_new (top_level, self->device);
159 g_signal_connect_object (self->apn_dialog, "unmap",
160 G_CALLBACK (wwan_apn_dialog_closed_cb),
161 self, G_CONNECT_SWAPPED);
162 }
163
164 gtk_widget_set_visible (GTK_WIDGET (self->apn_dialog), TRUE);
165 }
166
167 static GcrPrompt *
168 cc_wwan_device_page_new_prompt (CcWwanDevicePage *self,
169 MMModemLock lock)
170 {
171 GcrPrompt *prompt;
172 g_autoptr(GError) error = NULL;
173 g_autofree gchar *description = NULL;
174 g_autofree gchar *warning = NULL;
175 const gchar *message = NULL;
176 guint num;
177
178 prompt = GCR_PROMPT (gcr_system_prompt_open (-1, NULL, &error));
179
180 if (error)
181 {
182 g_warning ("Error opening Prompt: %s", error->message);
183 return NULL;
184 }
185
186 gcr_prompt_set_title (prompt, _("Unlock SIM card"));
187 gcr_prompt_set_continue_label (prompt, _("Unlock"));
188 gcr_prompt_set_cancel_label (prompt, _("Cancel"));
189
190 if (lock == MM_MODEM_LOCK_SIM_PIN)
191 {
192 description = g_strdup_printf (_("Please provide PIN code for SIM %d"), self->sim_index);
193 message = _("Enter PIN to unlock your SIM card");
194 }
195 else if (lock == MM_MODEM_LOCK_SIM_PUK)
196 {
197 description = g_strdup_printf (_("Please provide PUK code for SIM %d"), self->sim_index);
198 message = _("Enter PUK to unlock your SIM card");
199 }
200 else
201 {
202 g_warn_if_reached ();
203 g_object_unref (prompt);
204
205 return NULL;
206 }
207
208 gcr_prompt_set_description (prompt, description);
209 gcr_prompt_set_message (prompt, message);
210
211 num = cc_wwan_device_get_unlock_retries (self->device, lock);
212
213 if (num != MM_UNLOCK_RETRIES_UNKNOWN)
214 {
215 if (self->is_retry)
216 warning = g_strdup_printf (ngettext ("Wrong password entered. You have %1$u try left",
217 "Wrong password entered. You have %1$u tries left", num), num);
218 else
219 warning = g_strdup_printf (ngettext ("You have %u try left",
220 "You have %u tries left", num), num);
221 }
222 else if (self->is_retry)
223 {
224 warning = g_strdup (_("Wrong password entered."));
225 }
226
227 gcr_prompt_set_warning (prompt, warning);
228
229 return prompt;
230 }
231
232 static void
233 wwan_update_unlock_button (CcWwanDevicePage *self)
234 {
235 gtk_button_set_label (self->unlock_button, _("Unlock"));
236 gtk_widget_set_sensitive (GTK_WIDGET (self->unlock_button), TRUE);
237 }
238
239 static void
240 cc_wwan_device_page_unlocked_cb (GObject *object,
241 GAsyncResult *result,
242 gpointer user_data)
243 {
244 CcWwanDevicePage *self = user_data;
245 wwan_update_unlock_button (self);
246 }
247
248 static void
249 wwan_device_unlock_clicked_cb (CcWwanDevicePage *self)
250 {
251 g_autoptr(GError) error = NULL;
252 GcrPrompt *prompt;
253 const gchar *password, *warning;
254 const gchar *pin = "";
255 const gchar *puk = "";
256 MMModemLock lock;
257
258 lock = cc_wwan_device_get_lock (self->device);
259 password = "";
260
261 if (lock != MM_MODEM_LOCK_SIM_PIN &&
262 lock != MM_MODEM_LOCK_SIM_PUK)
263 g_return_if_reached ();
264
265 if (lock == MM_MODEM_LOCK_SIM_PUK)
266 {
267 prompt = cc_wwan_device_page_new_prompt (self, lock);
268
269 warning = _("PUK code should be an 8 digit number");
270 while (password && !cc_wwan_device_pin_valid (password, lock))
271 {
272 password = gcr_prompt_password (prompt, NULL, &error);
273 gcr_prompt_set_warning (prompt, warning);
274 }
275
276 puk = g_strdup (password);
277 password = "";
278 gcr_prompt_close (prompt);
279 g_object_unref (prompt);
280
281 if (error)
282 g_warning ("Error: %s", error->message);
283
284 /* Error or User cancelled PUK */
285 if (!puk)
286 return;
287 }
288
289 prompt = cc_wwan_device_page_new_prompt (self, MM_MODEM_LOCK_SIM_PIN);
290 if (lock == MM_MODEM_LOCK_SIM_PUK)
291 {
292 gcr_prompt_set_password_new (prompt, TRUE);
293 gcr_prompt_set_message (prompt, _("Enter New PIN"));
294 gcr_prompt_set_warning (prompt, "");
295 }
296
297 warning = _("PIN code should be a 4-8 digit number");
298 while (password && !cc_wwan_device_pin_valid (password, MM_MODEM_LOCK_SIM_PIN))
299 {
300 password = gcr_prompt_password (prompt, NULL, &error);
301 gcr_prompt_set_warning (prompt, warning);
302 }
303
304 pin = g_strdup (password);
305 gcr_prompt_close (prompt);
306 g_object_unref (prompt);
307
308 if (error)
309 g_warning ("Error: %s", error->message);
310
311 /* Error or User cancelled PIN */
312 if (!pin)
313 return;
314
315 gtk_button_set_label (self->unlock_button, _("Unlocking…"));
316 gtk_widget_set_sensitive (GTK_WIDGET (self->unlock_button), FALSE);
317
318 if (lock == MM_MODEM_LOCK_SIM_PIN)
319 cc_wwan_device_send_pin (self->device, pin,
320 NULL, /* cancellable */
321 cc_wwan_device_page_unlocked_cb,
322 self);
323 else if (lock == MM_MODEM_LOCK_SIM_PUK)
324 {
325 cc_wwan_device_send_puk (self->device, puk, pin,
326 NULL, /* Cancellable */
327 cc_wwan_device_page_unlocked_cb,
328 self);
329 }
330 else
331 {
332 g_warn_if_reached ();
333 }
334 }
335
336 static void
337 wwan_data_settings_changed_cb (CcWwanDevicePage *self,
338 GParamSpec *pspec,
339 AdwSwitchRow *data_row)
340 {
341 if (self->is_self_change)
342 {
343 self->is_self_change = FALSE;
344 return;
345 }
346
347 if (cc_wwan_data_get_default_apn (self->wwan_data) == NULL)
348 {
349 wwan_data_show_apn_dialog (self);
350 g_object_set_data (G_OBJECT (self->apn_dialog), "row", data_row);
351 }
352 else
353 {
354 wwan_device_page_handle_data_row (self, data_row);
355 }
356 }
357
358 static void
359 wwan_network_settings_activated_cb (CcWwanDevicePage *self,
360 CcListRow *row)
361 {
362 GtkWidget *dialog;
363 GtkWindow *top_level;
364
365 top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
366
367 if (row == self->network_mode_row)
368 {
369 if (!self->network_mode_dialog)
370 self->network_mode_dialog = cc_wwan_mode_dialog_new (top_level, self->device);
371
372 dialog = GTK_WIDGET (self->network_mode_dialog);
373 }
374 else if (row == self->network_name_row)
375 {
376 if (!self->network_dialog)
377 self->network_dialog = cc_wwan_network_dialog_new (top_level, self->device);
378
379 dialog = GTK_WIDGET (self->network_dialog);
380 }
381 else
382 {
383 return;
384 }
385
386 gtk_window_present (GTK_WINDOW (dialog));
387 }
388
389 static void
390 wwan_advanced_settings_activated_cb (CcWwanDevicePage *self,
391 CcListRow *row)
392 {
393 GtkWindow *top_level;
394
395 top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
396
397 if (row == self->sim_lock_row)
398 {
399 if (!self->sim_lock_dialog)
400 self->sim_lock_dialog = cc_wwan_sim_lock_dialog_new (top_level, self->device);
401 gtk_widget_set_visible (GTK_WIDGET (self->sim_lock_dialog), TRUE);
402 }
403 else if (row == self->details_row)
404 {
405 if (!self->details_dialog)
406 self->details_dialog = cc_wwan_details_dialog_new (top_level, self->device);
407 gtk_widget_set_visible (GTK_WIDGET (self->details_dialog), TRUE);
408 }
409 else if (row == self->apn_settings_row)
410 {
411 wwan_data_show_apn_dialog (self);
412 }
413 else
414 {
415 g_return_if_reached ();
416 }
417 }
418
419 static void
420 cc_wwan_device_page_update_data (CcWwanDevicePage *self)
421 {
422 gboolean has_data;
423
424 if (self->wwan_data == cc_wwan_device_get_data (self->device))
425 return;
426
427 self->wwan_data = cc_wwan_device_get_data (self->device);
428 has_data = self->wwan_data != NULL;
429
430 gtk_widget_set_sensitive (GTK_WIDGET (self->data_settings_list), has_data);
431 gtk_widget_set_sensitive (GTK_WIDGET (self->apn_settings_row), has_data);
432
433 if (!has_data)
434 return;
435
436 g_signal_handlers_block_by_func (self->data_roaming_row,
437 wwan_data_settings_changed_cb, self);
438 g_signal_handlers_block_by_func (self->data_enable_row,
439 wwan_data_settings_changed_cb, self);
440
441 g_object_set (self->data_roaming_row, "active",
442 cc_wwan_data_get_roaming_enabled (self->wwan_data), NULL);
443
444 g_object_set (self->data_enable_row, "active",
445 cc_wwan_data_get_enabled (self->wwan_data), NULL);
446
447 g_signal_handlers_unblock_by_func (self->data_roaming_row,
448 wwan_data_settings_changed_cb, self);
449 g_signal_handlers_unblock_by_func (self->data_enable_row,
450 wwan_data_settings_changed_cb, self);
451 }
452
453 static void
454 cc_wwan_device_page_update (CcWwanDevicePage *self)
455 {
456 GtkStack *main_stack;
457 MMModemLock lock;
458
459 main_stack = self->main_stack;
460 if (!cc_wwan_device_has_sim (self->device))
461 gtk_stack_set_visible_child_name (main_stack, "no-sim-view");
462 else if ((lock = cc_wwan_device_get_lock (self->device)) == MM_MODEM_LOCK_SIM_PIN ||
463 lock == MM_MODEM_LOCK_SIM_PUK)
464 gtk_stack_set_visible_child_name (main_stack, "sim-lock-view");
465 else
466 gtk_stack_set_visible_child_name (main_stack, "settings-view");
467 }
468
469 static void
470 cc_wwan_locks_changed_cb (CcWwanDevicePage *self)
471 {
472 const gchar *label;
473
474 if (cc_wwan_device_get_sim_lock (self->device))
475 label = _("Enabled");
476 else
477 label = _("Disabled");
478
479 cc_list_row_set_secondary_label (self->sim_lock_row, label);
480 cc_wwan_device_page_update (self);
481 }
482
483 static void
484 cc_wwan_device_page_set_property (GObject *object,
485 guint prop_id,
486 const GValue *value,
487 GParamSpec *pspec)
488 {
489 CcWwanDevicePage *self = (CcWwanDevicePage *)object;
490
491 switch (prop_id)
492 {
493 case PROP_DEVICE:
494 self->device = g_value_dup_object (value);
495 break;
496
497 default:
498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
499 }
500 }
501
502 static void
503 cc_wwan_device_page_constructed (GObject *object)
504 {
505 CcWwanDevicePage *self = (CcWwanDevicePage *)object;
506
507 G_OBJECT_CLASS (cc_wwan_device_page_parent_class)->constructed (object);
508
509 cc_wwan_device_page_update_data (self);
510
511 g_object_bind_property (self->device, "operator-name",
512 self->network_name_row, "secondary-label",
513 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
514
515 g_object_bind_property (self->device, "network-mode",
516 self->network_mode_row, "secondary-label",
517 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
518
519 g_signal_connect_object (self->device, "notify::enabled-locks",
520 (GCallback)cc_wwan_locks_changed_cb,
521 self, G_CONNECT_SWAPPED);
522
523 g_signal_connect_object (self->device, "notify::has-data",
524 (GCallback)cc_wwan_device_page_update_data,
525 self, G_CONNECT_SWAPPED);
526
527 cc_wwan_device_page_update (self);
528 cc_wwan_locks_changed_cb (self);
529 }
530
531 static void
532 cc_wwan_device_page_dispose (GObject *object)
533 {
534 CcWwanDevicePage *self = (CcWwanDevicePage *)object;
535
536 g_clear_pointer (&self->apn_dialog, gtk_window_destroy);
537 g_clear_pointer (&self->details_dialog, gtk_window_destroy);
538 g_clear_pointer (&self->network_mode_dialog, gtk_window_destroy);
539 g_clear_pointer (&self->network_dialog, gtk_window_destroy);
540 g_clear_pointer (&self->sim_lock_dialog, gtk_window_destroy);
541
542 g_clear_object (&self->wwan_proxy);
543 g_clear_object (&self->device);
544
545 G_OBJECT_CLASS (cc_wwan_device_page_parent_class)->dispose (object);
546 }
547
548 static void
549 cc_wwan_device_page_class_init (CcWwanDevicePageClass *klass)
550 {
551 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
552 GObjectClass *object_class = G_OBJECT_CLASS (klass);
553
554 object_class->set_property = cc_wwan_device_page_set_property;
555 object_class->constructed = cc_wwan_device_page_constructed;
556 object_class->dispose = cc_wwan_device_page_dispose;
557
558 g_type_ensure (CC_TYPE_WWAN_DEVICE);
559
560 properties[PROP_DEVICE] =
561 g_param_spec_object ("device",
562 "Device",
563 "The WWAN Device",
564 CC_TYPE_WWAN_DEVICE,
565 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
566
567 g_object_class_install_properties (object_class, N_PROPS, properties);
568
569 gtk_widget_class_set_template_from_resource (widget_class,
570 "/org/gnome/control-center/wwan/cc-wwan-device-page.ui");
571
572 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, advanced_settings_list);
573 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, apn_settings_row);
574 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_enable_row);
575 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_roaming_row);
576 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_settings_list);
577 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, details_row);
578 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, main_stack);
579 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_mode_row);
580 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_name_row);
581 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_settings_list);
582 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, sim_lock_row);
583 gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, unlock_button);
584
585 gtk_widget_class_bind_template_callback (widget_class, wwan_device_unlock_clicked_cb);
586 gtk_widget_class_bind_template_callback (widget_class, wwan_data_settings_changed_cb);
587 gtk_widget_class_bind_template_callback (widget_class, wwan_network_settings_activated_cb);
588 gtk_widget_class_bind_template_callback (widget_class, wwan_advanced_settings_activated_cb);
589 }
590
591 static void
592 cc_wwan_device_page_init (CcWwanDevicePage *self)
593 {
594 gtk_widget_init_template (GTK_WIDGET (self));
595 }
596
597 static void
598 cc_wwan_error_changed_cb (CcWwanDevicePage *self)
599 {
600 AdwToast *toast;
601 const gchar *message;
602
603 message = cc_wwan_device_get_simple_error (self->device);
604
605 if (!message)
606 return;
607
608 toast = adw_toast_new (message);
609 adw_toast_overlay_add_toast (self->toast_overlay, toast);
610 }
611
612 CcWwanDevicePage *
613 cc_wwan_device_page_new (CcWwanDevice *device,
614 GtkWidget *toast_overlay)
615 {
616 CcWwanDevicePage *self;
617
618 g_return_val_if_fail (CC_IS_WWAN_DEVICE (device), NULL);
619
620 self = g_object_new (CC_TYPE_WWAN_DEVICE_PAGE,
621 "device", device,
622 NULL);
623
624 self->toast_overlay = ADW_TOAST_OVERLAY (toast_overlay);
625
626 g_signal_connect_object (self->device, "notify::error",
627 G_CALLBACK (cc_wwan_error_changed_cb),
628 self, G_CONNECT_SWAPPED);
629
630 return self;
631 }
632
633 CcWwanDevice *
634 cc_wwan_device_page_get_device (CcWwanDevicePage *self)
635 {
636 g_return_val_if_fail (CC_IS_WWAN_DEVICE_PAGE (self), NULL);
637
638 return self->device;
639 }
640
641 void
642 cc_wwan_device_page_set_sim_index (CcWwanDevicePage *self,
643 gint sim_index)
644 {
645 g_return_if_fail (CC_IS_WWAN_DEVICE_PAGE (self));
646 g_return_if_fail (sim_index >= 1);
647
648 self->sim_index = sim_index;
649 }
650