LCOV - code coverage report
Current view: top level - src/plugins/connectivity_report - valent-connectivity_report-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 96.6 % 177 171
Test Date: 2024-11-22 21:57:18 Functions: 93.8 % 16 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 70.4 % 108 76

             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-connectivity_report-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <math.h>
       9                 :             : 
      10                 :             : #include <glib/gi18n.h>
      11                 :             : #include <gio/gio.h>
      12                 :             : #include <json-glib/json-glib.h>
      13                 :             : #include <valent.h>
      14                 :             : 
      15                 :             : #include "valent-connectivity_report-plugin.h"
      16                 :             : #include "valent-telephony.h"
      17                 :             : 
      18                 :             : 
      19                 :             : struct _ValentConnectivityReportPlugin
      20                 :             : {
      21                 :             :   ValentDevicePlugin  parent_instance;
      22                 :             : 
      23                 :             :   /* Local Modems */
      24                 :             :   ValentTelephony    *telephony;
      25                 :             :   unsigned int        telephony_watch : 1;
      26                 :             : };
      27                 :             : 
      28                 :             : static void   valent_connectivity_report_plugin_send_state    (ValentConnectivityReportPlugin *self);
      29                 :             : 
      30   [ +  +  +  - ]:         106 : G_DEFINE_FINAL_TYPE (ValentConnectivityReportPlugin, valent_connectivity_report_plugin, VALENT_TYPE_DEVICE_PLUGIN)
      31                 :             : 
      32                 :             : 
      33                 :             : /*
      34                 :             :  * Local Modems
      35                 :             :  */
      36                 :             : static void
      37                 :           4 : on_telephony_changed (ValentTelephony                *telephony,
      38                 :             :                       ValentConnectivityReportPlugin *self)
      39                 :             : {
      40         [ +  - ]:           4 :   g_assert (VALENT_IS_TELEPHONY (telephony));
      41         [ -  + ]:           4 :   g_assert (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
      42                 :             : 
      43                 :           4 :   valent_connectivity_report_plugin_send_state (self);
      44                 :           4 : }
      45                 :             : 
      46                 :             : 
      47                 :             : static void
      48                 :          17 : valent_connectivity_report_plugin_watch_telephony (ValentConnectivityReportPlugin *self,
      49                 :             :                                                    gboolean                        watch)
      50                 :             : {
      51         [ +  - ]:          17 :   g_assert (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
      52                 :             : 
      53         [ +  + ]:          17 :   if (self->telephony_watch == watch)
      54                 :             :     return;
      55                 :             : 
      56         [ +  + ]:           4 :   if (self->telephony == NULL)
      57                 :           2 :     self->telephony = valent_telephony_get_default ();
      58                 :             : 
      59         [ +  + ]:           4 :   if (watch)
      60                 :             :     {
      61                 :           2 :       g_signal_connect_object (self->telephony,
      62                 :             :                                "changed",
      63                 :             :                                G_CALLBACK (on_telephony_changed),
      64                 :             :                                self, 0);
      65                 :           2 :       self->telephony_watch = TRUE;
      66                 :             :     }
      67                 :             :   else
      68                 :             :     {
      69                 :           2 :       g_signal_handlers_disconnect_by_data (self->telephony, self);
      70                 :           2 :       self->telephony_watch = FALSE;
      71                 :             :     }
      72                 :             : }
      73                 :             : 
      74                 :             : static void
      75                 :           4 : valent_connectivity_report_plugin_send_state (ValentConnectivityReportPlugin *self)
      76                 :             : {
      77                 :           4 :   GSettings *settings;
      78                 :           4 :   g_autoptr (JsonBuilder) builder = NULL;
      79         [ -  + ]:           4 :   g_autoptr (JsonNode) packet = NULL;
      80   [ +  -  -  - ]:           4 :   g_autoptr (JsonNode) signal_node = NULL;
      81                 :             : 
      82         [ +  - ]:           4 :   g_return_if_fail (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
      83                 :             : 
      84                 :           4 :   settings = valent_extension_get_settings (VALENT_EXTENSION (self));
      85                 :             : 
      86         [ +  - ]:           4 :   if (!g_settings_get_boolean (settings, "share-state"))
      87                 :             :     return;
      88                 :             : 
      89                 :           4 :   signal_node = valent_telephony_get_signal_strengths (self->telephony);
      90                 :             : 
      91                 :           4 :   valent_packet_init (&builder, "kdeconnect.connectivity_report");
      92                 :           4 :   json_builder_set_member_name (builder, "signalStrengths");
      93                 :           4 :   json_builder_add_value (builder, g_steal_pointer (&signal_node));
      94                 :           4 :   packet = valent_packet_end (&builder);
      95                 :             : 
      96         [ +  - ]:           4 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
      97                 :             : }
      98                 :             : 
      99                 :             : 
     100                 :             : /*
     101                 :             :  * Remote Modems
     102                 :             :  */
     103                 :             : static const char *
     104                 :           8 : get_network_type_icon (const char *network_type)
     105                 :             : {
     106         [ +  + ]:           8 :   if (g_str_equal (network_type, "GSM") ||
     107         [ +  - ]:           7 :       g_str_equal (network_type, "CDMA") ||
     108         [ +  - ]:           7 :       g_str_equal (network_type, "iDEN"))
     109                 :             :     return "network-cellular-2g-symbolic";
     110                 :             : 
     111         [ +  + ]:           7 :   if (g_str_equal (network_type, "UMTS") ||
     112         [ +  - ]:           6 :       g_str_equal (network_type, "CDMA2000"))
     113                 :             :     return "network-cellular-3g-symbolic";
     114                 :             : 
     115         [ +  + ]:           6 :   if (g_str_equal (network_type, "EDGE"))
     116                 :             :     return "network-cellular-edge-symbolic";
     117                 :             : 
     118         [ +  + ]:           5 :   if (g_str_equal (network_type, "GPRS"))
     119                 :             :     return "network-cellular-gprs-symbolic";
     120                 :             : 
     121         [ +  + ]:           4 :   if (g_str_equal (network_type, "HSPA"))
     122                 :             :     return "network-cellular-hspa-symbolic";
     123                 :             : 
     124         [ +  + ]:           3 :   if (g_str_equal (network_type, "LTE"))
     125                 :             :     return "network-cellular-4g-symbolic";
     126                 :             : 
     127         [ +  + ]:           2 :   if (g_str_equal (network_type, "5G"))
     128                 :           1 :     return "network-cellular-5g-symbolic";
     129                 :             : 
     130                 :             :   return "network-cellular-symbolic";
     131                 :             : }
     132                 :             : 
     133                 :             : static const char *
     134                 :           9 : get_signal_strength_icon (double signal_strength)
     135                 :             : {
     136         [ +  + ]:           9 :   if (signal_strength >= 4.0)
     137                 :             :     return "network-cellular-signal-excellent-symbolic";
     138                 :             : 
     139         [ +  + ]:           6 :   if (signal_strength >= 3.0)
     140                 :             :     return "network-cellular-signal-good-symbolic";
     141                 :             : 
     142         [ +  + ]:           5 :   if (signal_strength >= 2.0)
     143                 :             :     return "network-cellular-signal-ok-symbolic";
     144                 :             : 
     145         [ +  + ]:           4 :   if (signal_strength >= 1.0)
     146                 :             :     return "network-cellular-signal-weak-symbolic";
     147                 :             : 
     148         [ +  + ]:           3 :   if (signal_strength >= 0.0)
     149                 :           1 :     return "network-cellular-signal-none-symbolic";
     150                 :             : 
     151                 :             :   return "network-cellular-offline-symbolic";
     152                 :             : }
     153                 :             : 
     154                 :             : static void
     155                 :           9 : get_status_labels (double   signal_strength,
     156                 :             :                    char   **status_title,
     157                 :             :                    char   **status_body)
     158                 :             : {
     159         [ +  + ]:           9 :   if (signal_strength >= 1.0)
     160                 :             :     {
     161                 :             :       /* TRANSLATORS: When the mobile network signal is available */
     162         [ -  + ]:           6 :       *status_title = g_strdup (_("Mobile Network"));
     163                 :             :       /* TRANSLATORS: The mobile network signal strength (e.g. "Signal Strength (25%)") */
     164                 :           6 :       *status_body = g_strdup_printf (_("Signal Strength %f%%"),
     165                 :             :                                       floor (signal_strength * 20.0));
     166                 :             :     }
     167         [ +  + ]:           3 :   else if (signal_strength >= 0.0)
     168                 :             :     {
     169                 :             :       /* TRANSLATORS: When no mobile service is available */
     170         [ -  + ]:           1 :       *status_title = g_strdup (_("No Service"));
     171                 :             :       /* TRANSLATORS: When no mobile network signal is available */
     172         [ -  + ]:           2 :       *status_body = g_strdup (_("No mobile network service"));
     173                 :             :     }
     174                 :             :   else
     175                 :             :     {
     176                 :             :       /* TRANSLATORS: When no mobile service is available */
     177         [ -  + ]:           2 :       *status_title = g_strdup (_("No Service"));
     178                 :             :       /* TRANSLATORS: When the device is missing a SIM card */
     179         [ -  + ]:           4 :       *status_body = g_strdup (_("No SIM"));
     180                 :             :     }
     181                 :           9 : }
     182                 :             : 
     183                 :             : static void
     184                 :           9 : valent_connectivity_report_plugin_handle_connectivity_report (ValentConnectivityReportPlugin *self,
     185                 :             :                                                               JsonNode                       *packet)
     186                 :             : {
     187                 :           9 :   GAction *action;
     188                 :           9 :   GVariant *state;
     189                 :           9 :   GSettings *settings;
     190                 :           9 :   GVariantBuilder builder;
     191                 :           9 :   GVariantBuilder signals_builder;
     192                 :           9 :   JsonObject *signal_strengths;
     193                 :           9 :   JsonObjectIter iter;
     194                 :           9 :   const char *signal_id;
     195                 :           9 :   JsonNode *signal_node;
     196                 :           9 :   double average_strength = 0.0;
     197                 :           9 :   double n_nodes = 0;
     198                 :           9 :   gboolean is_online = FALSE;
     199                 :           9 :   const char *status_icon;
     200                 :           9 :   g_autofree char *status_title = NULL;
     201                 :           9 :   g_autofree char *status_body = NULL;
     202                 :             : 
     203         [ +  - ]:           9 :   g_assert (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
     204         [ -  + ]:           9 :   g_assert (VALENT_IS_PACKET (packet));
     205                 :             : 
     206         [ -  + ]:           9 :   if (!valent_packet_get_object (packet, "signalStrengths", &signal_strengths))
     207                 :             :     {
     208                 :           0 :       g_debug ("%s(): expected \"signalStrengths\" field holding an object",
     209                 :             :                G_STRFUNC);
     210                 :           0 :       return;
     211                 :             :     }
     212                 :             : 
     213                 :           9 :   settings = valent_extension_get_settings (VALENT_EXTENSION (self));
     214                 :             : 
     215                 :             :   /* Add each signal */
     216                 :           9 :   g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
     217                 :           9 :   g_variant_builder_init (&signals_builder, G_VARIANT_TYPE_VARDICT);
     218                 :             : 
     219                 :           9 :   json_object_iter_init (&iter, signal_strengths);
     220                 :             : 
     221         [ +  + ]:          17 :   while (json_object_iter_next (&iter, &signal_id, &signal_node))
     222                 :             :     {
     223                 :           8 :       GVariantBuilder signal_builder;
     224                 :           8 :       JsonObject *signal_obj;
     225                 :           8 :       const char *network_type;
     226                 :           8 :       int64_t signal_strength;
     227                 :           8 :       const char *icon_name;
     228                 :             : 
     229         [ +  - ]:           8 :       if G_UNLIKELY (json_node_get_value_type (signal_node) != JSON_TYPE_OBJECT)
     230                 :             :         {
     231                 :           0 :           g_debug ("%s(): expected entry value holding an object",
     232                 :             :                    G_STRFUNC);
     233                 :           0 :           continue;
     234                 :             :         }
     235                 :             : 
     236                 :             :       /* Extract the signal information */
     237                 :           8 :       signal_obj = json_node_get_object (signal_node);
     238                 :           8 :       network_type = json_object_get_string_member_with_default (signal_obj,
     239                 :             :                                                                  "networkType",
     240                 :             :                                                                  "Unknown");
     241                 :           8 :       signal_strength = json_object_get_int_member_with_default (signal_obj,
     242                 :             :                                                                  "signalStrength",
     243                 :             :                                                                  -1);
     244                 :           8 :       icon_name = get_network_type_icon (network_type);
     245                 :             : 
     246                 :             :       /* Ignore offline modems (`-1`) when determining the average strength */
     247         [ +  + ]:           8 :       if (signal_strength >= 0)
     248                 :             :         {
     249                 :           7 :           average_strength = (n_nodes * average_strength + signal_strength) /
     250                 :           7 :                              (n_nodes + 1);
     251                 :           7 :           n_nodes += 1;
     252                 :           7 :           is_online = TRUE;
     253                 :             :         }
     254                 :             : 
     255                 :             :       /* Add the signal to the `signal_strengths` dictionary */
     256                 :           8 :       g_variant_builder_init (&signal_builder, G_VARIANT_TYPE_VARDICT);
     257                 :           8 :       g_variant_builder_add (&signal_builder, "{sv}", "network-type",
     258                 :             :                              g_variant_new_string (network_type));
     259                 :           8 :       g_variant_builder_add (&signal_builder, "{sv}", "signal-strength",
     260                 :             :                              g_variant_new_int64 (signal_strength));
     261                 :           8 :       g_variant_builder_add (&signal_builder, "{sv}", "icon-name",
     262                 :             :                              g_variant_new_string (icon_name));
     263                 :           8 :       g_variant_builder_add (&signals_builder, "{sv}", signal_id,
     264                 :             :                              g_variant_builder_end (&signal_builder));
     265                 :             :     }
     266                 :             : 
     267                 :           9 :   g_variant_builder_add (&builder, "{sv}", "signal-strengths",
     268                 :             :                          g_variant_builder_end (&signals_builder));
     269                 :             : 
     270                 :             :   /* Set the status properties */
     271         [ +  + ]:           9 :   status_icon = get_signal_strength_icon (is_online ? average_strength : -1);
     272                 :           9 :   get_status_labels (is_online ? average_strength : -1,
     273                 :             :                      &status_title,
     274                 :             :                      &status_body);
     275                 :             : 
     276                 :           9 :   g_variant_builder_add (&builder, "{sv}", "icon-name",
     277                 :             :                          g_variant_new_string (status_icon));
     278                 :           9 :   g_variant_builder_add (&builder, "{sv}", "title",
     279                 :             :                          g_variant_new_string (status_title));
     280                 :           9 :   g_variant_builder_add (&builder, "{sv}", "body",
     281                 :             :                          g_variant_new_string (status_body));
     282                 :             : 
     283                 :           9 :   state = g_variant_builder_end (&builder);
     284                 :             : 
     285                 :             :   /* Update the GAction */
     286                 :           9 :   action = g_action_map_lookup_action (G_ACTION_MAP (self), "state");
     287                 :           9 :   g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
     288                 :           9 :                                json_object_get_size (signal_strengths) > 0);
     289                 :           9 :   g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
     290                 :             : 
     291                 :             :   /* Notify if necessary */
     292         [ +  + ]:           9 :   if (average_strength > 0.0)
     293                 :             :     {
     294                 :           6 :       valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self),
     295                 :             :                                               "offline");
     296                 :             :     }
     297         [ +  - ]:           3 :   else if (g_settings_get_boolean (settings, "offline-notification"))
     298                 :             :     {
     299                 :           3 :       ValentDevice *device;
     300                 :           9 :       g_autoptr (GNotification) notification = NULL;
     301         [ +  - ]:           3 :       g_autoptr (GIcon) icon = NULL;
     302         [ +  - ]:           3 :       g_autofree char *title = NULL;
     303                 :           3 :       g_autofree char *body = NULL;
     304                 :           3 :       const char *device_name;
     305                 :             : 
     306                 :           3 :       device = valent_extension_get_object (VALENT_EXTENSION (self));
     307                 :           3 :       device_name = valent_device_get_name (device);
     308                 :             : 
     309                 :             :       /* TRANSLATORS: The connectivity notification title (e.g. "PinePhone: No Service") */
     310                 :           3 :       title = g_strdup_printf (_("%s: %s"), device_name, status_title);
     311                 :             :       /* TRANSLATORS: The connectivity notification body (e.g. "No mobile network service") */
     312         [ -  + ]:           3 :       body = g_strdup (status_body);
     313                 :           3 :       icon = g_themed_icon_new (status_icon);
     314                 :             : 
     315                 :           3 :       notification = g_notification_new (title);
     316                 :           3 :       g_notification_set_body (notification, body);
     317                 :           3 :       g_notification_set_icon (notification, icon);
     318                 :           3 :       valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     319                 :             :                                               "offline",
     320                 :             :                                               notification);
     321                 :             :     }
     322                 :             : }
     323                 :             : 
     324                 :             : /*
     325                 :             :  * GActions
     326                 :             :  */
     327                 :             : static void
     328                 :           0 : state_action (GSimpleAction *action,
     329                 :             :               GVariant      *parameter,
     330                 :             :               gpointer       user_data)
     331                 :             : {
     332                 :             :   // No-op to make the state read-only
     333                 :           0 : }
     334                 :             : 
     335                 :             : static const GActionEntry actions[] = {
     336                 :             :     {"state", NULL, NULL, "@a{sv} {}", state_action},
     337                 :             : };
     338                 :             : 
     339                 :             : /*
     340                 :             :  * ValentDevicePlugin
     341                 :             :  */
     342                 :             : static void
     343                 :           9 : valent_connectivity_report_plugin_update_state (ValentDevicePlugin *plugin,
     344                 :             :                                                 ValentDeviceState   state)
     345                 :             : {
     346                 :           9 :   ValentConnectivityReportPlugin *self = VALENT_CONNECTIVITY_REPORT_PLUGIN (plugin);
     347                 :           9 :   gboolean available;
     348                 :             : 
     349         [ +  - ]:           9 :   g_assert (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
     350                 :             : 
     351                 :           9 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
     352                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
     353                 :             : 
     354         [ +  + ]:           9 :   if (available)
     355                 :             :     {
     356                 :           2 :       valent_connectivity_report_plugin_watch_telephony (self, TRUE);
     357                 :             :     }
     358                 :             :   else
     359                 :             :     {
     360                 :           7 :       valent_connectivity_report_plugin_watch_telephony (self, FALSE);
     361                 :           7 :       valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available);
     362                 :             :     }
     363                 :           9 : }
     364                 :             : 
     365                 :             : static void
     366                 :           9 : valent_connectivity_report_plugin_handle_packet (ValentDevicePlugin *plugin,
     367                 :             :                                                  const char         *type,
     368                 :             :                                                  JsonNode           *packet)
     369                 :             : {
     370                 :           9 :   ValentConnectivityReportPlugin *self = VALENT_CONNECTIVITY_REPORT_PLUGIN (plugin);
     371                 :             : 
     372         [ +  - ]:           9 :   g_assert (VALENT_IS_CONNECTIVITY_REPORT_PLUGIN (self));
     373         [ -  + ]:           9 :   g_assert (type != NULL);
     374         [ -  + ]:           9 :   g_assert (VALENT_IS_PACKET (packet));
     375                 :             : 
     376                 :             :   /* A remote connectivity report */
     377         [ +  - ]:           9 :   if (g_str_equal (type, "kdeconnect.connectivity_report"))
     378                 :           9 :     valent_connectivity_report_plugin_handle_connectivity_report (self, packet);
     379                 :             :   else
     380                 :           9 :     g_assert_not_reached ();
     381                 :           9 : }
     382                 :             : 
     383                 :             : /*
     384                 :             :  * ValentObject
     385                 :             :  */
     386                 :             : static void
     387                 :           8 : valent_connectivity_report_plugin_destroy (ValentObject *object)
     388                 :             : {
     389                 :           8 :   ValentConnectivityReportPlugin *self = VALENT_CONNECTIVITY_REPORT_PLUGIN (object);
     390                 :             : 
     391                 :           8 :   valent_connectivity_report_plugin_watch_telephony (self, FALSE);
     392                 :             : 
     393                 :           8 :   VALENT_OBJECT_CLASS (valent_connectivity_report_plugin_parent_class)->destroy (object);
     394                 :           8 : }
     395                 :             : 
     396                 :             : /*
     397                 :             :  * GObject
     398                 :             :  */
     399                 :             : static void
     400                 :           4 : valent_connectivity_report_plugin_constructed (GObject *object)
     401                 :             : {
     402                 :           4 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
     403                 :             : 
     404                 :           4 :   g_action_map_add_action_entries (G_ACTION_MAP (plugin),
     405                 :             :                                    actions,
     406                 :             :                                    G_N_ELEMENTS (actions),
     407                 :             :                                    plugin);
     408                 :             : 
     409                 :           4 :   G_OBJECT_CLASS (valent_connectivity_report_plugin_parent_class)->constructed (object);
     410                 :           4 : }
     411                 :             : 
     412                 :             : static void
     413                 :          18 : valent_connectivity_report_plugin_class_init (ValentConnectivityReportPluginClass *klass)
     414                 :             : {
     415                 :          18 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     416                 :          18 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
     417                 :          18 :   ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
     418                 :             : 
     419                 :          18 :   object_class->constructed = valent_connectivity_report_plugin_constructed;
     420                 :             : 
     421                 :          18 :   vobject_class->destroy = valent_connectivity_report_plugin_destroy;
     422                 :             : 
     423                 :          18 :   plugin_class->handle_packet = valent_connectivity_report_plugin_handle_packet;
     424                 :          18 :   plugin_class->update_state = valent_connectivity_report_plugin_update_state;
     425                 :             : }
     426                 :             : 
     427                 :             : static void
     428                 :           4 : valent_connectivity_report_plugin_init (ValentConnectivityReportPlugin *self)
     429                 :             : {
     430                 :           4 : }
     431                 :             : 
        

Generated by: LCOV version 2.0-1