[gnome-software] gs-shell: Add an info bar about automatic updates



commit 184ec1bf02d9ff45b818e39c6d99826a146de0eb
Author: Philip Withnall <withnall endlessm com>
Date:   Thu May 3 17:43:50 2018 +0200

    gs-shell: Add an info bar about automatic updates
    
    If automatic updates are disabled due to being on a metered network,
    inform the user using an info bar which is visible at the top of the
    main window, regardless of which tab the user is on.
    
    Give the user a button to open the control centre and adjust their
    network settings to, for example, unmark the current network as metered.
    
    The bar is hidden if automatic updates are enabled.
    
    If Mogwai is used, the main gnome-software window will hold the Mogwai
    daemon open while the window is visible, to ensure it gets updates on
    whether automatic updates are enabled. That prevents the daemon
    automatically exiting. The hold is released when the main window is
    closed (and gnome-software continues to run in the background) and is
    reinstated when the main window is next reopened.
    
    The bar works regardless of whether Mogwai is used, but its criteria for
    being shown differ slightly.
    
    This is based on work originally done by Joaquim Rocha for the Endless
    fork of gnome-software.

 po/POTFILES.in                   |   1 +
 src/gnome-software.gresource.xml |   1 +
 src/gnome-software.ui            | 190 ++++++++++++++++++++-----------
 src/gs-metered-data-dialog.c     |  68 +++++++++++
 src/gs-metered-data-dialog.h     |  22 ++++
 src/gs-metered-data-dialog.ui    |  69 +++++++++++
 src/gs-shell.c                   | 240 +++++++++++++++++++++++++++++++++++++++
 src/meson.build                  |   1 +
 8 files changed, 525 insertions(+), 67 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f3fe65bf..7622db86 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,7 @@ src/gs-installed-page.ui
 src/gs-loading-page.c
 src/gs-loading-page.ui
 src/gs-main.c
+src/gs-metered-data-dialog.ui
 src/gs-moderate-page.ui
 src/gs-origin-popover-row.c
 src/gs-origin-popover-row.ui
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 01ba394d..3eaabca2 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -14,6 +14,7 @@
   <file preprocess="xml-stripblanks">gs-info-bar.ui</file>
   <file preprocess="xml-stripblanks">gs-installed-page.ui</file>
   <file preprocess="xml-stripblanks">gs-loading-page.ui</file>
+  <file preprocess="xml-stripblanks">gs-metered-data-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-moderate-page.ui</file>
   <file preprocess="xml-stripblanks">gs-overview-page.ui</file>
   <file preprocess="xml-stripblanks">gs-origin-popover-row.ui</file>
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index decb44e0..6646e092 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -438,85 +438,141 @@
             </child>
 
             <child>
-              <object class="GtkStack" id="stack_main">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="homogeneous">False</property>
-                <child>
-                  <object class="GsOverviewPage" id="overview_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">overview</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsInstalledPage" id="installed_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">installed</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsModeratePage" id="moderate_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">moderate</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsLoadingPage" id="loading_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">loading</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsSearchPage" id="search_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">search</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsUpdatesPage" id="updates_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">updates</property>
-                  </packing>
-                </child>
+                <property name="orientation">vertical</property>
 
                 <child>
-                  <object class="GsDetailsPage" id="details_page">
+                  <object class="GtkInfoBar" id="metered_updates_bar">
                     <property name="visible">True</property>
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">12</property>
+                    <property name="message-type">GTK_MESSAGE_INFO</property>
+                    <property name="show-close-button">False</property>
+                    <property name="revealed">False</property>
+                    <child internal-child="content_area">
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <property name="margin_top">6</property>
+                        <property name="margin_left">6</property>
+                        <property name="margin_bottom">6</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Automatic Updates Paused</property>
+                            <property name="xalign">0.0</property>
+                            <attributes>
+                              <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+                            </attributes>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child internal-child="action_area">
+                      <object class="GtkButtonBox">
+                        <property name="visible">True</property>
+                        <property name="margin_right">6</property>
+                        <child>
+                          <object class="GtkButton" id="metered_updates_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="label" translatable="yes">Find Out _More</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <action-widgets>
+                      <action-widget response="GTK_RESPONSE_OK">metered_updates_button</action-widget>
+                    </action-widgets>
                   </object>
-                  <packing>
-                    <property name="name">details</property>
-                  </packing>
                 </child>
 
                 <child>
-                  <object class="GsCategoryPage" id="category_page">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="name">category</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GsExtrasPage" id="extras_page">
+                  <object class="GtkStack" id="stack_main">
                     <property name="visible">True</property>
+                    <property name="homogeneous">False</property>
+                    <child>
+                      <object class="GsOverviewPage" id="overview_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">overview</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsInstalledPage" id="installed_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">installed</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsModeratePage" id="moderate_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">moderate</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsLoadingPage" id="loading_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">loading</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsSearchPage" id="search_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">search</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsUpdatesPage" id="updates_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">updates</property>
+                      </packing>
+                    </child>
+
+                    <child>
+                      <object class="GsDetailsPage" id="details_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">details</property>
+                      </packing>
+                    </child>
+
+                    <child>
+                      <object class="GsCategoryPage" id="category_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">category</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GsExtrasPage" id="extras_page">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="name">extras</property>
+                      </packing>
+                    </child>
                   </object>
-                  <packing>
-                    <property name="name">extras</property>
-                  </packing>
                 </child>
               </object>
-
             </child>
           </object>
         </child>
diff --git a/src/gs-metered-data-dialog.c b/src/gs-metered-data-dialog.c
new file mode 100644
index 00000000..d91c0c2e
--- /dev/null
+++ b/src/gs-metered-data-dialog.c
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2020 Endless Mobile, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gs-metered-data-dialog.h"
+
+struct _GsMeteredDataDialog
+{
+       GtkDialog        parent_instance;
+
+       GtkWidget       *button_network_settings;
+};
+
+G_DEFINE_TYPE (GsMeteredDataDialog, gs_metered_data_dialog, GTK_TYPE_DIALOG)
+
+static void
+button_network_settings_clicked_cb (GtkButton *button,
+                                   gpointer   user_data)
+{
+       g_autoptr(GError) error_local = NULL;
+
+       if (!g_spawn_command_line_async ("gnome-control-center wifi", &error_local)) {
+               g_warning ("Error opening GNOME Control Center: %s",
+                          error_local->message);
+               return;
+       }
+}
+
+static void
+gs_metered_data_dialog_init (GsMeteredDataDialog *dialog)
+{
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+gs_metered_data_dialog_class_init (GsMeteredDataDialogClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-metered-data-dialog.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsMeteredDataDialog, button_network_settings);
+
+       gtk_widget_class_bind_template_callback (widget_class, button_network_settings_clicked_cb);
+}
+
+GtkWidget *
+gs_metered_data_dialog_new (GtkWindow *parent)
+{
+       GsMeteredDataDialog *dialog;
+
+       dialog = g_object_new (GS_TYPE_METERED_DATA_DIALOG,
+                              "use-header-bar", TRUE,
+                              "transient-for", parent,
+                              "modal", TRUE,
+                              NULL);
+
+       return GTK_WIDGET (dialog);
+}
diff --git a/src/gs-metered-data-dialog.h b/src/gs-metered-data-dialog.h
new file mode 100644
index 00000000..62eecd47
--- /dev/null
+++ b/src/gs-metered-data-dialog.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2020 Endless Mobile, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_METERED_DATA_DIALOG (gs_metered_data_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsMeteredDataDialog, gs_metered_data_dialog, GS, METERED_DATA_DIALOG, GtkDialog)
+
+GtkWidget      *gs_metered_data_dialog_new     (GtkWindow      *parent);
+
+G_END_DECLS
diff --git a/src/gs-metered-data-dialog.ui b/src/gs-metered-data-dialog.ui
new file mode 100644
index 00000000..def0243b
--- /dev/null
+++ b/src/gs-metered-data-dialog.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GsMeteredDataDialog" parent="GtkDialog">
+    <property name="title" translatable="yes">Automatic Updates Paused</property>
+    <property name="modal">True</property>
+    <property name="destroy-with-parent">True</property>
+    <property name="resizable">False</property>
+    <property name="type-hint">dialog</property>
+    <property name="skip-taskbar-hint">True</property>
+    <property name="use-header-bar">1</property>
+    <child internal-child="headerbar">
+      <object class="GtkHeaderBar">
+        <child type="title">
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Automatic Updates Paused</property>
+            <property name="selectable">False</property>
+            <style>
+              <class name="title"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <property name="margin">12</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="justify">fill</property>
+                <property name="wrap">True</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="label" translatable="yes">The current network is metered. Metered 
connections have data limits or charges associated with them. To save data, automatic updates have therefore 
been paused.
+
+Automatic updates will be resumed when an un­metered network becomes available. Until then, it is still 
possible to manually install updates.
+
+Alternatively, if the current network has been in­correctly identified as being metered, this setting can be 
changed.</property>
+                <property name="max-width-chars">40</property>
+                <property name="halign">center</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <child type="center">
+                  <object class="GtkButton" id="button_network_settings">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Open Network _Settings</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">True</property>
+                    <property name="use-underline">True</property>
+                    <signal name="clicked" handler="button_network_settings_clicked_cb"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="fill">False</property>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+          </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 173a8e48..009776ad 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -12,10 +12,15 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
+#ifdef HAVE_MOGWAI
+#include <libmogwai-schedule-client/scheduler.h>
+#endif
+
 #include "gs-common.h"
 #include "gs-shell.h"
 #include "gs-details-page.h"
 #include "gs-installed-page.h"
+#include "gs-metered-data-dialog.h"
 #include "gs-moderate-page.h"
 #include "gs-loading-page.h"
 #include "gs-search-page.h"
@@ -51,6 +56,7 @@ typedef struct {
 
 typedef struct
 {
+       GSettings               *settings;
        gboolean                 ignore_primary_buttons;
        GCancellable            *cancellable;
        GsPluginLoader          *plugin_loader;
@@ -66,6 +72,11 @@ typedef struct
        gchar                   *events_info_uri;
        gboolean                 in_mode_change;
        GsPage                  *page;
+
+#ifdef HAVE_MOGWAI
+       MwscScheduler           *scheduler;
+       gulong                   scheduler_invalidated_handler;
+#endif  /* HAVE_MOGWAI */
 } GsShellPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GsShell, gs_shell, G_TYPE_OBJECT)
@@ -186,6 +197,171 @@ gs_shell_set_header_end_widget (GsShell *shell, GtkWidget *widget)
        }
 }
 
+static void
+gs_shell_refresh_auto_updates_ui (GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       gboolean automatic_updates_paused;
+       gboolean automatic_updates_enabled;
+       GtkInfoBar *metered_updates_bar;
+
+       automatic_updates_enabled = g_settings_get_boolean (priv->settings, "download-updates");
+
+       metered_updates_bar = GTK_INFO_BAR (gtk_builder_get_object (priv->builder, "metered_updates_bar"));
+
+#ifdef HAVE_MOGWAI
+       automatic_updates_paused = (priv->scheduler == NULL || !mwsc_scheduler_get_allow_downloads 
(priv->scheduler));
+#else
+       automatic_updates_paused = gs_plugin_loader_get_network_metered (priv->plugin_loader);
+#endif
+
+       gtk_info_bar_set_revealed (metered_updates_bar,
+                                  priv->mode != GS_SHELL_MODE_LOADING &&
+                                  automatic_updates_enabled &&
+                                  automatic_updates_paused);
+       gtk_info_bar_set_default_response (metered_updates_bar, GTK_RESPONSE_OK);
+}
+
+static void
+gs_shell_metered_updates_bar_response_cb (GtkInfoBar *info_bar,
+                                         gint        response_id,
+                                         gpointer    user_data)
+{
+       GsShell *shell = GS_SHELL (user_data);
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       GtkDialog *dialog;
+
+       dialog = GTK_DIALOG (gs_metered_data_dialog_new (priv->main_window));
+       gs_shell_modal_dialog_present (shell, dialog);
+
+       /* just destroy */
+       g_signal_connect_swapped (dialog, "response",
+                                 G_CALLBACK (gtk_widget_destroy), dialog);
+}
+
+static void
+gs_shell_download_updates_changed_cb (GSettings   *settings,
+                                     const gchar *key,
+                                     gpointer     user_data)
+{
+       GsShell *shell = user_data;
+
+       gs_shell_refresh_auto_updates_ui (shell);
+}
+
+static void
+gs_shell_network_metered_notify_cb (GsPluginLoader *plugin_loader,
+                                   GParamSpec     *pspec,
+                                   gpointer        user_data)
+{
+#ifndef HAVE_MOGWAI
+       GsShell *shell = user_data;
+
+       /* @automatic_updates_paused only depends on network-metered if we’re
+        * compiled without Mogwai. */
+       gs_shell_refresh_auto_updates_ui (shell);
+#endif
+}
+
+#ifdef HAVE_MOGWAI
+static void
+scheduler_invalidated_cb (GsShell *shell)
+{
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+
+       /* The scheduler shouldn’t normally be invalidated, since we Hold() it
+        * until we’re done with it. However, if the scheduler is stopped by
+        * systemd (`systemctl stop mogwai-scheduled`) this signal will be
+        * emitted. It may also be invalidated while our main window is hidden,
+        * as we release our Hold() then. */
+       g_signal_handler_disconnect (priv->scheduler,
+                                    priv->scheduler_invalidated_handler);
+       priv->scheduler_invalidated_handler = 0;
+
+       g_clear_object (&priv->scheduler);
+}
+
+static void
+scheduler_allow_downloads_changed_cb (GsShell *shell)
+{
+       gs_shell_refresh_auto_updates_ui (shell);
+}
+
+static void
+scheduler_hold_cb (GObject *source_object,
+                  GAsyncResult *result,
+                  gpointer data)
+{
+       g_autoptr(GError) error_local = NULL;
+       MwscScheduler *scheduler = (MwscScheduler *) source_object;
+       g_autoptr(GsShell) shell = data;  /* reference added when starting the async operation */
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+
+       if (!mwsc_scheduler_hold_finish (scheduler, result, &error_local)) {
+               g_warning ("Couldn't hold the Mogwai Scheduler daemon: %s",
+                          error_local->message);
+               return;
+       }
+
+       priv->scheduler_invalidated_handler =
+               g_signal_connect_swapped (scheduler, "invalidated",
+                                         (GCallback) scheduler_invalidated_cb,
+                                         shell);
+
+       g_signal_connect_object (scheduler, "notify::allow-downloads",
+                                (GCallback) scheduler_allow_downloads_changed_cb,
+                                shell,
+                                G_CONNECT_SWAPPED);
+
+       g_assert (priv->scheduler == NULL);
+       priv->scheduler = scheduler;
+
+       /* Update the UI accordingly. */
+       gs_shell_refresh_auto_updates_ui (shell);
+}
+
+static void
+scheduler_release_cb (GObject *source_object,
+                     GAsyncResult *result,
+                     gpointer data)
+{
+       MwscScheduler *scheduler = (MwscScheduler *) source_object;
+       g_autoptr(GsShell) shell = data;  /* reference added when starting the async operation */
+       GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+       g_autoptr(GError) error_local = NULL;
+
+       if (!mwsc_scheduler_release_finish (scheduler, result, &error_local))
+               g_warning ("Couldn't release the Mogwai Scheduler daemon: %s",
+                          error_local->message);
+
+       g_clear_object (&priv->scheduler);
+}
+
+static void
+scheduler_ready_cb (GObject *source_object,
+                   GAsyncResult *result,
+                   gpointer data)
+{
+       MwscScheduler *scheduler;
+       g_autoptr(GError) error_local = NULL;
+       g_autoptr(GsShell) shell = data;  /* reference added when starting the async operation */
+
+       scheduler = mwsc_scheduler_new_finish (result, &error_local);
+
+       if (scheduler == NULL) {
+               g_warning ("%s: Error getting Mogwai Scheduler: %s", G_STRFUNC,
+                          error_local->message);
+               return;
+       }
+
+       mwsc_scheduler_hold_async (scheduler,
+                                  "monitoring allow-downloads property",
+                                  NULL,
+                                  scheduler_hold_cb,
+                                  g_object_ref (shell));
+}
+#endif  /* HAVE_MOGWAI */
+
 static void
 free_back_entry (BackEntry *entry)
 {
@@ -347,6 +523,16 @@ gs_shell_change_mode (GsShell *shell,
        widget = gs_page_get_header_end_widget (page);
        gs_shell_set_header_end_widget (shell, widget);
 
+       /* refresh the updates bar when moving out of the loading mode, but only
+        * if the Mogwai scheduler state is already known, to avoid spuriously
+        * showing the updates bar */
+#ifdef HAVE_MOGWAI
+       if (priv->scheduler != NULL)
+#else
+       if (TRUE)
+#endif
+               gs_shell_refresh_auto_updates_ui (shell);
+
        /* destroy any existing modals */
        if (priv->modal_dialogs != NULL) {
                gsize i = 0;
@@ -740,6 +926,21 @@ main_window_closed_cb (GtkWidget *dialog, GdkEvent *event, gpointer user_data)
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "notification_event"));
        gtk_revealer_set_reveal_child (GTK_REVEALER (widget), FALSE);
 
+       /* release our hold on the download scheduler */
+#ifdef HAVE_MOGWAI
+       if (priv->scheduler != NULL) {
+               if (priv->scheduler_invalidated_handler > 0)
+                       g_signal_handler_disconnect (priv->scheduler,
+                                                    priv->scheduler_invalidated_handler);
+               priv->scheduler_invalidated_handler = 0;
+
+               mwsc_scheduler_release_async (priv->scheduler,
+                                             NULL,
+                                             scheduler_release_cb,
+                                             g_object_ref (shell));
+       }
+#endif  /* HAVE_MOGWAI */
+
        gs_shell_clean_back_entry_stack (shell);
        gtk_widget_hide (dialog);
        return TRUE;
@@ -751,6 +952,18 @@ gs_shell_main_window_mapped_cb (GtkWidget *widget, GsShell *shell)
        GsShellPrivate *priv = gs_shell_get_instance_private (shell);
        gs_plugin_loader_set_scale (priv->plugin_loader,
                                    (guint) gtk_widget_get_scale_factor (widget));
+
+       /* Set up the updates bar. Do this here rather than in gs_shell_setup()
+        * since we only want to hold the scheduler open while the gnome-software
+        * main window is visible, and not while we’re running in the background. */
+#ifdef HAVE_MOGWAI
+       if (priv->scheduler == NULL)
+               mwsc_scheduler_new_async (priv->cancellable,
+                                         (GAsyncReadyCallback) scheduler_ready_cb,
+                                         g_object_ref (shell));
+#else
+       gs_shell_refresh_auto_updates_ui (shell);
+#endif  /* HAVE_MOGWAI */
 }
 
 static void
@@ -1910,8 +2123,13 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
        g_signal_connect_object (priv->plugin_loader, "notify::allow-updates",
                                 G_CALLBACK (gs_shell_allow_updates_notify_cb),
                                 shell, 0);
+       g_signal_connect_object (priv->plugin_loader, "notify::network-metered",
+                                G_CALLBACK (gs_shell_network_metered_notify_cb),
+                                shell, 0);
        priv->cancellable = g_object_ref (cancellable);
 
+       priv->settings = g_settings_new ("org.gnome.software");
+
        /* get UI */
        priv->builder = gtk_builder_new_from_resource ("/org/gnome/Software/gnome-software.ui");
        priv->main_window = GTK_WINDOW (gtk_builder_get_object (priv->builder, "window_software"));
@@ -2025,6 +2243,14 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
        g_hash_table_insert (priv->pages, g_strdup ("extras"), page);
        gs_shell_setup_pages (shell);
 
+       /* set up the metered data info bar and mogwai */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "metered_updates_bar"));
+       g_signal_connect (widget, "response",
+                         (GCallback) gs_shell_metered_updates_bar_response_cb, shell);
+
+       g_signal_connect (priv->settings, "changed::download-updates",
+                         (GCallback) gs_shell_download_updates_changed_cb, shell);
+
        /* set up search */
        g_signal_connect (priv->main_window, "key-press-event",
                          G_CALLBACK (window_keypress_handler), shell);
@@ -2233,6 +2459,20 @@ gs_shell_dispose (GObject *object)
        g_clear_pointer (&priv->pages, g_hash_table_unref);
        g_clear_pointer (&priv->events_info_uri, g_free);
        g_clear_pointer (&priv->modal_dialogs, g_ptr_array_unref);
+       g_clear_object (&priv->settings);
+
+#ifdef HAVE_MOGWAI
+       if (priv->scheduler != NULL) {
+               if (priv->scheduler_invalidated_handler > 0)
+                       g_signal_handler_disconnect (priv->scheduler,
+                                                    priv->scheduler_invalidated_handler);
+
+               mwsc_scheduler_release_async (priv->scheduler,
+                                             NULL,
+                                             scheduler_release_cb,
+                                             g_object_ref (shell));
+       }
+#endif  /* HAVE_MOGWAI */
 
        G_OBJECT_CLASS (gs_shell_parent_class)->dispose (object);
 }
diff --git a/src/meson.build b/src/meson.build
index 300ae952..cbd0a511 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -38,6 +38,7 @@ gnome_software_sources = [
   'gs-language.c',
   'gs-loading-page.c',
   'gs-main.c',
+  'gs-metered-data-dialog.c',
   'gs-moderate-page.c',
   'gs-overview-page.c',
   'gs-origin-popover-row.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]