diff options
Diffstat (limited to 'panels/wwan/cc-wwan-device-page.c')
-rw-r--r-- | panels/wwan/cc-wwan-device-page.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/panels/wwan/cc-wwan-device-page.c b/panels/wwan/cc-wwan-device-page.c new file mode 100644 index 0000000..13aa87a --- /dev/null +++ b/panels/wwan/cc-wwan-device-page.c @@ -0,0 +1,650 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* cc-wwan-device-page.c + * + * Copyright 2019 Purism SPC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author(s): + * Mohammed Sadiq <sadiq@sadiqpk.org> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cc-wwan-device-page" + +#include <config.h> +#include <glib/gi18n.h> +#include <libmm-glib.h> +#define GCR_API_SUBJECT_TO_CHANGE +#include <gcr/gcr-base.h> + +#include "cc-list-row.h" +#include "cc-wwan-data.h" +#include "cc-wwan-mode-dialog.h" +#include "cc-wwan-network-dialog.h" +#include "cc-wwan-details-dialog.h" +#include "cc-wwan-sim-lock-dialog.h" +#include "cc-wwan-apn-dialog.h" +#include "cc-wwan-device-page.h" +#include "cc-wwan-resources.h" + +#include "shell/cc-application.h" +#include "shell/cc-debug.h" +#include "shell/cc-object-storage.h" + +/** + * @short_description: Device settings page + * @include: "cc-wwan-device-page.h" + * + * The Device page allows users to configure device + * settings. Please note that there is no one-to-one + * maping for a device settings page and a physical + * device. Say, if a device have two SIM card slots, + * there should be two device pages, one for each SIM. + */ + +struct _CcWwanDevicePage +{ + GtkBox parent_instance; + + GtkListBox *advanced_settings_list; + CcListRow *apn_settings_row; + CcListRow *data_enable_row; + CcListRow *data_roaming_row; + GtkListBox *data_settings_list; + CcListRow *details_row; + GtkStack *main_stack; + CcListRow *network_mode_row; + CcListRow *network_name_row; + GtkListBox *network_settings_list; + CcListRow *sim_lock_row; + GtkButton *unlock_button; + + AdwToastOverlay *toast_overlay; + + CcWwanDevice *device; + CcWwanData *wwan_data; + GDBusProxy *wwan_proxy; + + CcWwanApnDialog *apn_dialog; + CcWwanDetailsDialog *details_dialog; + CcWwanModeDialog *network_mode_dialog; + CcWwanNetworkDialog *network_dialog; + CcWwanSimLockDialog *sim_lock_dialog; + + gint sim_index; + /* Set if a change is triggered in a signal’s callback, + * to avoid re-triggering of callback. This is used + * instead of blocking handlers where the signal may be + * emitted async and the block/unblock may not work right + */ + gboolean is_self_change; + gboolean is_retry; +}; + +G_DEFINE_TYPE (CcWwanDevicePage, cc_wwan_device_page, GTK_TYPE_BOX) + +enum { + PROP_0, + PROP_DEVICE, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS]; + +static void +wwan_device_page_handle_data_row (CcWwanDevicePage *self, + CcListRow *data_row) +{ + gboolean active; + + /* The user dismissed the dialog for selecting default APN */ + if (cc_wwan_data_get_default_apn (self->wwan_data) == NULL) + { + self->is_self_change = TRUE; + gtk_widget_activate (GTK_WIDGET (data_row)); + + return; + } + + active = cc_list_row_get_active (data_row); + + if (data_row == self->data_enable_row) + cc_wwan_data_set_enabled (self->wwan_data, active); + else + cc_wwan_data_set_roaming_enabled (self->wwan_data, active); + + cc_wwan_data_save_settings (self->wwan_data, NULL, NULL, NULL); +} + +static gboolean +wwan_apn_dialog_closed_cb (CcWwanDevicePage *self) +{ + CcListRow *data_row; + + if (gtk_widget_in_destruction (GTK_WIDGET (self))) + return FALSE; + + data_row = g_object_get_data (G_OBJECT (self->apn_dialog), "row"); + g_object_set_data (G_OBJECT (self->apn_dialog), "row", NULL); + + if (data_row) + wwan_device_page_handle_data_row (self, data_row); + + return FALSE; +} + +static void +wwan_data_show_apn_dialog (CcWwanDevicePage *self) +{ + GtkWindow *top_level; + + top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))); + + if (!self->apn_dialog) + { + self->apn_dialog = cc_wwan_apn_dialog_new (top_level, self->device); + g_signal_connect_object (self->apn_dialog, "unmap", + G_CALLBACK (wwan_apn_dialog_closed_cb), + self, G_CONNECT_SWAPPED); + } + + gtk_widget_show (GTK_WIDGET (self->apn_dialog)); +} + +static GcrPrompt * +cc_wwan_device_page_new_prompt (CcWwanDevicePage *self, + MMModemLock lock) +{ + GcrPrompt *prompt; + g_autoptr(GError) error = NULL; + g_autofree gchar *description = NULL; + g_autofree gchar *warning = NULL; + const gchar *message = NULL; + guint num; + + prompt = GCR_PROMPT (gcr_system_prompt_open (-1, NULL, &error)); + + if (error) + { + g_warning ("Error opening Prompt: %s", error->message); + return NULL; + } + + gcr_prompt_set_title (prompt, _("Unlock SIM card")); + gcr_prompt_set_continue_label (prompt, _("Unlock")); + gcr_prompt_set_cancel_label (prompt, _("Cancel")); + + if (lock == MM_MODEM_LOCK_SIM_PIN) + { + description = g_strdup_printf (_("Please provide PIN code for SIM %d"), self->sim_index); + message = _("Enter PIN to unlock your SIM card"); + } + else if (lock == MM_MODEM_LOCK_SIM_PUK) + { + description = g_strdup_printf (_("Please provide PUK code for SIM %d"), self->sim_index); + message = _("Enter PUK to unlock your SIM card"); + } + else + { + g_warn_if_reached (); + g_object_unref (prompt); + + return NULL; + } + + gcr_prompt_set_description (prompt, description); + gcr_prompt_set_message (prompt, message); + + num = cc_wwan_device_get_unlock_retries (self->device, lock); + + if (num != MM_UNLOCK_RETRIES_UNKNOWN) + { + if (self->is_retry) + warning = g_strdup_printf (ngettext ("Wrong password entered. You have %1$u try left", + "Wrong password entered. You have %1$u tries left", num), num); + else + warning = g_strdup_printf (ngettext ("You have %u try left", + "You have %u tries left", num), num); + } + else if (self->is_retry) + { + warning = g_strdup (_("Wrong password entered.")); + } + + gcr_prompt_set_warning (prompt, warning); + + return prompt; +} + +static void +wwan_update_unlock_button (CcWwanDevicePage *self) +{ + gtk_button_set_label (self->unlock_button, _("Unlock")); + gtk_widget_set_sensitive (GTK_WIDGET (self->unlock_button), TRUE); +} + +static void +cc_wwan_device_page_unlocked_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + CcWwanDevicePage *self = user_data; + wwan_update_unlock_button (self); +} + +static void +wwan_device_unlock_clicked_cb (CcWwanDevicePage *self) +{ + g_autoptr(GError) error = NULL; + GcrPrompt *prompt; + const gchar *password, *warning; + const gchar *pin = ""; + const gchar *puk = ""; + MMModemLock lock; + + lock = cc_wwan_device_get_lock (self->device); + password = ""; + + if (lock != MM_MODEM_LOCK_SIM_PIN && + lock != MM_MODEM_LOCK_SIM_PUK) + g_return_if_reached (); + + if (lock == MM_MODEM_LOCK_SIM_PUK) + { + prompt = cc_wwan_device_page_new_prompt (self, lock); + + warning = _("PUK code should be an 8 digit number"); + while (password && !cc_wwan_device_pin_valid (password, lock)) + { + password = gcr_prompt_password (prompt, NULL, &error); + gcr_prompt_set_warning (prompt, warning); + } + + puk = g_strdup (password); + password = ""; + gcr_prompt_close (prompt); + g_object_unref (prompt); + + if (error) + g_warning ("Error: %s", error->message); + + /* Error or User cancelled PUK */ + if (!puk) + return; + } + + prompt = cc_wwan_device_page_new_prompt (self, MM_MODEM_LOCK_SIM_PIN); + if (lock == MM_MODEM_LOCK_SIM_PUK) + { + gcr_prompt_set_password_new (prompt, TRUE); + gcr_prompt_set_message (prompt, _("Enter New PIN")); + gcr_prompt_set_warning (prompt, ""); + } + + warning = _("PIN code should be a 4-8 digit number"); + while (password && !cc_wwan_device_pin_valid (password, MM_MODEM_LOCK_SIM_PIN)) + { + password = gcr_prompt_password (prompt, NULL, &error); + gcr_prompt_set_warning (prompt, warning); + } + + pin = g_strdup (password); + gcr_prompt_close (prompt); + g_object_unref (prompt); + + if (error) + g_warning ("Error: %s", error->message); + + /* Error or User cancelled PIN */ + if (!pin) + return; + + gtk_button_set_label (self->unlock_button, _("Unlocking…")); + gtk_widget_set_sensitive (GTK_WIDGET (self->unlock_button), FALSE); + + if (lock == MM_MODEM_LOCK_SIM_PIN) + cc_wwan_device_send_pin (self->device, pin, + NULL, /* cancellable */ + cc_wwan_device_page_unlocked_cb, + self); + else if (lock == MM_MODEM_LOCK_SIM_PUK) + { + cc_wwan_device_send_puk (self->device, puk, pin, + NULL, /* Cancellable */ + cc_wwan_device_page_unlocked_cb, + self); + } + else + { + g_warn_if_reached (); + } +} + +static void +wwan_data_settings_changed_cb (CcWwanDevicePage *self, + GParamSpec *pspec, + CcListRow *data_row) +{ + if (self->is_self_change) + { + self->is_self_change = FALSE; + return; + } + + if (cc_wwan_data_get_default_apn (self->wwan_data) == NULL) + { + wwan_data_show_apn_dialog (self); + g_object_set_data (G_OBJECT (self->apn_dialog), "row", data_row); + } + else + { + wwan_device_page_handle_data_row (self, data_row); + } +} + +static void +wwan_network_settings_activated_cb (CcWwanDevicePage *self, + CcListRow *row) +{ + GtkWidget *dialog; + GtkWindow *top_level; + + top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))); + + if (row == self->network_mode_row) + { + if (!self->network_mode_dialog) + self->network_mode_dialog = cc_wwan_mode_dialog_new (top_level, self->device); + + dialog = GTK_WIDGET (self->network_mode_dialog); + } + else if (row == self->network_name_row) + { + if (!self->network_dialog) + self->network_dialog = cc_wwan_network_dialog_new (top_level, self->device); + + dialog = GTK_WIDGET (self->network_dialog); + } + else + { + return; + } + + gtk_widget_show (dialog); +} + +static void +wwan_advanced_settings_activated_cb (CcWwanDevicePage *self, + CcListRow *row) +{ + GtkWindow *top_level; + + top_level = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))); + + if (row == self->sim_lock_row) + { + if (!self->sim_lock_dialog) + self->sim_lock_dialog = cc_wwan_sim_lock_dialog_new (top_level, self->device); + gtk_widget_show (GTK_WIDGET (self->sim_lock_dialog)); + } + else if (row == self->details_row) + { + if (!self->details_dialog) + self->details_dialog = cc_wwan_details_dialog_new (top_level, self->device); + gtk_widget_show (GTK_WIDGET (self->details_dialog)); + } + else if (row == self->apn_settings_row) + { + wwan_data_show_apn_dialog (self); + } + else + { + g_return_if_reached (); + } +} + +static void +cc_wwan_device_page_update_data (CcWwanDevicePage *self) +{ + gboolean has_data; + + if (self->wwan_data == cc_wwan_device_get_data (self->device)) + return; + + self->wwan_data = cc_wwan_device_get_data (self->device); + has_data = self->wwan_data != NULL; + + gtk_widget_set_sensitive (GTK_WIDGET (self->data_settings_list), has_data); + gtk_widget_set_sensitive (GTK_WIDGET (self->apn_settings_row), has_data); + + if (!has_data) + return; + + g_signal_handlers_block_by_func (self->data_roaming_row, + wwan_data_settings_changed_cb, self); + g_signal_handlers_block_by_func (self->data_enable_row, + wwan_data_settings_changed_cb, self); + + g_object_set (self->data_roaming_row, "active", + cc_wwan_data_get_roaming_enabled (self->wwan_data), NULL); + + g_object_set (self->data_enable_row, "active", + cc_wwan_data_get_enabled (self->wwan_data), NULL); + + g_signal_handlers_unblock_by_func (self->data_roaming_row, + wwan_data_settings_changed_cb, self); + g_signal_handlers_unblock_by_func (self->data_enable_row, + wwan_data_settings_changed_cb, self); +} + +static void +cc_wwan_device_page_update (CcWwanDevicePage *self) +{ + GtkStack *main_stack; + MMModemLock lock; + + main_stack = self->main_stack; + if (!cc_wwan_device_has_sim (self->device)) + gtk_stack_set_visible_child_name (main_stack, "no-sim-view"); + else if ((lock = cc_wwan_device_get_lock (self->device)) == MM_MODEM_LOCK_SIM_PIN || + lock == MM_MODEM_LOCK_SIM_PUK) + gtk_stack_set_visible_child_name (main_stack, "sim-lock-view"); + else + gtk_stack_set_visible_child_name (main_stack, "settings-view"); +} + +static void +cc_wwan_locks_changed_cb (CcWwanDevicePage *self) +{ + const gchar *label; + + if (cc_wwan_device_get_sim_lock (self->device)) + label = _("Enabled"); + else + label = _("Disabled"); + + cc_list_row_set_secondary_label (self->sim_lock_row, label); + cc_wwan_device_page_update (self); +} + +static void +cc_wwan_device_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcWwanDevicePage *self = (CcWwanDevicePage *)object; + + switch (prop_id) + { + case PROP_DEVICE: + self->device = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_wwan_device_page_constructed (GObject *object) +{ + CcWwanDevicePage *self = (CcWwanDevicePage *)object; + + G_OBJECT_CLASS (cc_wwan_device_page_parent_class)->constructed (object); + + cc_wwan_device_page_update_data (self); + + g_object_bind_property (self->device, "operator-name", + self->network_name_row, "secondary-label", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + g_object_bind_property (self->device, "network-mode", + self->network_mode_row, "secondary-label", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + g_signal_connect_object (self->device, "notify::enabled-locks", + (GCallback)cc_wwan_locks_changed_cb, + self, G_CONNECT_SWAPPED); + + g_signal_connect_object (self->device, "notify::has-data", + (GCallback)cc_wwan_device_page_update_data, + self, G_CONNECT_SWAPPED); + + cc_wwan_device_page_update (self); + cc_wwan_locks_changed_cb (self); +} + +static void +cc_wwan_device_page_dispose (GObject *object) +{ + CcWwanDevicePage *self = (CcWwanDevicePage *)object; + + g_clear_pointer ((GtkWindow **)&self->apn_dialog, gtk_window_destroy); + g_clear_pointer ((GtkWindow **)&self->details_dialog, gtk_window_destroy); + g_clear_pointer ((GtkWindow **)&self->network_mode_dialog, gtk_window_destroy); + g_clear_pointer ((GtkWindow **)&self->network_dialog, gtk_window_destroy); + g_clear_pointer ((GtkWindow **)&self->sim_lock_dialog, gtk_window_destroy); + + g_clear_object (&self->wwan_proxy); + g_clear_object (&self->device); + + G_OBJECT_CLASS (cc_wwan_device_page_parent_class)->dispose (object); +} + +static void +cc_wwan_device_page_class_init (CcWwanDevicePageClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = cc_wwan_device_page_set_property; + object_class->constructed = cc_wwan_device_page_constructed; + object_class->dispose = cc_wwan_device_page_dispose; + + g_type_ensure (CC_TYPE_WWAN_DEVICE); + + properties[PROP_DEVICE] = + g_param_spec_object ("device", + "Device", + "The WWAN Device", + CC_TYPE_WWAN_DEVICE, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/control-center/wwan/cc-wwan-device-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, advanced_settings_list); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, apn_settings_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_enable_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_roaming_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, data_settings_list); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, details_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, main_stack); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_mode_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_name_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, network_settings_list); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, sim_lock_row); + gtk_widget_class_bind_template_child (widget_class, CcWwanDevicePage, unlock_button); + + gtk_widget_class_bind_template_callback (widget_class, wwan_device_unlock_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, wwan_data_settings_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, wwan_network_settings_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, wwan_advanced_settings_activated_cb); +} + +static void +cc_wwan_device_page_init (CcWwanDevicePage *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +cc_wwan_error_changed_cb (CcWwanDevicePage *self) +{ + AdwToast *toast; + const gchar *message; + + message = cc_wwan_device_get_simple_error (self->device); + + if (!message) + return; + + toast = adw_toast_new (message); + adw_toast_overlay_add_toast (self->toast_overlay, toast); +} + +CcWwanDevicePage * +cc_wwan_device_page_new (CcWwanDevice *device, + GtkWidget *toast_overlay) +{ + CcWwanDevicePage *self; + + g_return_val_if_fail (CC_IS_WWAN_DEVICE (device), NULL); + + self = g_object_new (CC_TYPE_WWAN_DEVICE_PAGE, + "device", device, + NULL); + + self->toast_overlay = ADW_TOAST_OVERLAY (toast_overlay); + + g_signal_connect_object (self->device, "notify::error", + G_CALLBACK (cc_wwan_error_changed_cb), + self, G_CONNECT_SWAPPED); + + return self; +} + +CcWwanDevice * +cc_wwan_device_page_get_device (CcWwanDevicePage *self) +{ + g_return_val_if_fail (CC_IS_WWAN_DEVICE_PAGE (self), NULL); + + return self->device; +} + +void +cc_wwan_device_page_set_sim_index (CcWwanDevicePage *self, + gint sim_index) +{ + g_return_if_fail (CC_IS_WWAN_DEVICE_PAGE (self)); + g_return_if_fail (sim_index >= 1); + + self->sim_index = sim_index; +} |