[gnome-software] Update to the latest designs that allow refreshing of the update lists



commit 7a0ba32ae01aef387673c962931e6eee784c9b41
Author: Richard Hughes <richard hughsie com>
Date:   Mon Jan 27 17:15:30 2014 +0000

    Update to the latest designs that allow refreshing of the update lists

 src/gnome-software.ui                      |  167 ++++++++++-
 src/gs-plugin-loader-sync.c                |   52 +++-
 src/gs-plugin-loader-sync.h                |    7 +-
 src/gs-plugin-loader.c                     |  223 +++++++++++++-
 src/gs-plugin-loader.h                     |   11 +-
 src/gs-plugin.h                            |   17 +-
 src/gs-shell-details.c                     |    2 +-
 src/gs-shell-updates.c                     |  484 ++++++++++++++++++++++++++--
 src/gs-shell.c                             |    7 +-
 src/plugins/Makefile.am                    |    9 +
 src/plugins/gs-plugin-packagekit-refresh.c |  171 ++++++++++
 11 files changed, 1114 insertions(+), 36 deletions(-)
---
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index b6f3c45..9ffbe97 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -22,6 +22,11 @@
     <property name="icon_size">0</property>
     <property name="icon_name">object-select-symbolic</property> 
   </object>
+  <object class="GtkImage" id="button_refresh_image">
+    <property name="visible">True</property>
+    <property name="icon_size">0</property>
+    <property name="icon_name">view-refresh-symbolic</property> 
+  </object>
   <object class="GtkApplicationWindow" id="window_software">
     <property name="can_focus">False</property>
     <property name="default-width">1200</property>
@@ -246,7 +251,7 @@
               </object>
             </child>
             <child>
-              <object class="GtkSpinner" id="header_spinner">
+              <object class="GtkSpinner" id="header_spinner_end">
                 <property name="can_focus">False</property>
               </object>
               <packing>
@@ -311,6 +316,23 @@
                 <property name="pack_type">end</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkButton" id="button_refresh">
+                <property name="image">button_refresh_image</property>
+                <property name="can_focus">True</property>
+              </object>
+              <packing>
+                <property name="pack_type">start</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinner" id="header_spinner_start">
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="pack_type">start</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -808,14 +830,39 @@
                   </object>
                 </child>
                 <child>
-                  <object class="GtkSpinner" id="spinner_updates">
+                  <object class="GtkBox" id="updates_spinner_box">
                     <property name="visible">True</property>
-                    <property name="width_request">128</property>
-                    <property name="height_request">128</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
                     <property name="halign">center</property>
                     <property name="valign">center</property>
                     <property name="hexpand">True</property>
                     <property name="vexpand">True</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                    <child>
+                      <object class="GtkSpinner" id="spinner_updates">
+                        <property name="visible">True</property>
+                        <property name="width_request">128</property>
+                        <property name="height_request">128</property>
+                        <property name="halign">center</property>
+                        <property name="valign">center</property>
+                        <property name="hexpand">True</property>
+                        <property name="vexpand">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_updates_spinner">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label"></property>
+                        <property name="justify">center</property>
+                        <attributes>
+                          <attribute name="scale" value="1.4"/>
+                        </attributes>
+                      </object>
+                    </child>
                   </object>
                   <packing>
                     <property name="name">spinner</property>
@@ -890,6 +937,118 @@
                     <property name="name">uptodate</property>
                   </packing>
                 </child>
+
+                <child>
+                  <object class="GtkBox" id="updates_mobile_box">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage" id="image_updates_mobile">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="pixel_size">128</property>
+                        <property name="icon_name">dialog-warning-symbolic</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_updates_mobile">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Checking for updates when using mobile 
broadband could cause you to incur charges</property>
+                        <property name="wrap">True</property>
+                        <property name="halign">center</property>
+                        <property name="max-width-chars">40</property>
+                        <property name="justify">center</property>
+                        <attributes>
+                          <attribute name="scale" value="1.4"/>
+                        </attributes>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="button_updates_mobile">
+                        <property name="label" translatable="yes">_Check Anyway</property>
+                        <property name="width_request">150</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="hexpand">False</property>
+                        <property name="halign">center</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="name">mobile</property>
+                  </packing>
+                </child>
+
+                <child>
+                  <object class="GtkBox" id="updates_offline_box">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                    <child>
+                      <object class="GtkImage" id="image_updates_offline">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="pixel_size">128</property>
+                        <property name="icon_name">network-offline-symbolic</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_updates_offline">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Go online to check for updates</property>
+                        <property name="wrap">True</property>
+                        <property name="halign">center</property>
+                        <property name="justify">center</property>
+                        <attributes>
+                          <attribute name="scale" value="1.4"/>
+                        </attributes>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="button_updates_offline">
+                        <property name="label" translatable="yes">_Network Settings</property>
+                        <property name="width_request">150</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="hexpand">False</property>
+                        <property name="halign">center</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="name">offline</property>
+                  </packing>
+                </child>
+
                 <child>
                   <object class="GtkBox" id="updates_failed_box">
                     <property name="visible">True</property>
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index 74ada43..efd2253 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2012-2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2012-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -473,4 +473,54 @@ gs_plugin_loader_app_action (GsPluginLoader *plugin_loader,
        return helper.ret;
 }
 
+/**
+ * gs_plugin_loader_refresh_finish_sync:
+ **/
+static void
+gs_plugin_loader_refresh_finish_sync (GsPluginLoader *plugin_loader,
+                                     GAsyncResult *res,
+                                     GsPluginLoaderHelper *helper)
+{
+       helper->ret = gs_plugin_loader_refresh_finish (plugin_loader,
+                                                      res,
+                                                      helper->error);
+       g_main_loop_quit (helper->loop);
+}
+
+/**
+ * gs_plugin_loader_refresh:
+ **/
+gboolean
+gs_plugin_loader_refresh (GsPluginLoader *plugin_loader,
+                         guint cache_age,
+                         GsPluginRefreshFlags flags,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       GsPluginLoaderHelper helper;
+
+       /* create temp object */
+       helper.context = g_main_context_new ();
+       helper.loop = g_main_loop_new (helper.context, FALSE);
+       helper.error = error;
+
+       g_main_context_push_thread_default (helper.context);
+
+       /* run async method */
+       gs_plugin_loader_refresh_async (plugin_loader,
+                                       cache_age,
+                                       flags,
+                                       cancellable,
+                                       (GAsyncReadyCallback) gs_plugin_loader_refresh_finish_sync,
+                                       &helper);
+       g_main_loop_run (helper.loop);
+
+       g_main_context_pop_thread_default (helper.context);
+
+       g_main_loop_unref (helper.loop);
+       g_main_context_unref (helper.context);
+
+       return helper.ret;
+}
+
 /* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index 187983f..64f940c 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2007-2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -68,6 +68,11 @@ gboolean      gs_plugin_loader_app_action            (GsPluginLoader *plugin_loader,
                                                         GsPluginLoaderAction action,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_loader_refresh               (GsPluginLoader *plugin_loader,
+                                                        guint           cache_age,
+                                                        GsPluginRefreshFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 GsApp          *gs_plugin_loader_get_app_by_id         (GsPluginLoader *plugin_loader,
                                                         const gchar    *id,
                                                         GsPluginRefineFlags flags,
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 946b5ad..9334630 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2007-2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -644,6 +644,7 @@ typedef struct {
        GsPluginLoader                  *plugin_loader;
        GsPluginRefineFlags              flags;
        gchar                           *value;
+       guint                            cache_age;
        GsCategory                      *category;
        GsApp                           *app;
        GsAppState                       state_progress;
@@ -2775,4 +2776,224 @@ gs_plugin_loader_set_network_status (GsPluginLoader *plugin_loader,
        g_list_free_full (queue, g_object_unref);
 }
 
+/******************************************************************************/
+
+/**
+ * gs_plugin_loader_run_refresh_plugin:
+ **/
+static gboolean
+gs_plugin_loader_run_refresh_plugin (GsPluginLoader *plugin_loader,
+                                    GsPlugin *plugin,
+                                    guint cache_age,
+                                    GsPluginRefreshFlags flags,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       const gchar *function_name = "gs_plugin_refresh";
+       gboolean exists;
+       gboolean ret = TRUE;
+       gchar *profile_id = NULL;
+       GError *error_local = NULL;
+       GsPluginRefreshFunc plugin_func = NULL;
+
+       exists = g_module_symbol (plugin->module,
+                                 function_name,
+                                 (gpointer *) &plugin_func);
+       if (!exists)
+               goto out;
+       profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                     plugin->name, function_name);
+       gs_profile_start (plugin_loader->priv->profile, profile_id);
+       ret = plugin_func (plugin, cache_age, flags, cancellable, &error_local);
+       if (!ret) {
+               if (g_error_matches (error_local,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NOT_SUPPORTED)) {
+                       ret = TRUE;
+                       g_debug ("not supported for plugin %s: %s",
+                                plugin->name,
+                                error_local->message);
+                       g_clear_error (&error_local);
+               } else {
+                       g_propagate_error (error, error_local);
+                       goto out;
+               }
+       }
+out:
+       if (profile_id != NULL) {
+               gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+       }
+       g_free (profile_id);
+       return ret;
+}
+
+/**
+ * gs_plugin_loader_run_refresh:
+ **/
+static gboolean
+gs_plugin_loader_run_refresh (GsPluginLoader *plugin_loader,
+                             guint cache_age,
+                             GsPluginRefreshFlags flags,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       gboolean anything_ran = FALSE;
+       gboolean ret = TRUE;
+       GsPlugin *plugin;
+       guint i;
+
+       /* run each plugin */
+       for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
+               plugin = g_ptr_array_index (plugin_loader->priv->plugins, i);
+               if (!plugin->enabled)
+                       continue;
+               ret = g_cancellable_set_error_if_cancelled (cancellable, error);
+               if (ret) {
+                       ret = FALSE;
+                       goto out;
+               }
+               ret = gs_plugin_loader_run_refresh_plugin (plugin_loader,
+                                                          plugin,
+                                                          cache_age,
+                                                          flags,
+                                                          cancellable,
+                                                          error);
+               if (!ret)
+                       goto out;
+               anything_ran = TRUE;
+       }
+
+       /* nothing ran */
+       if (!anything_ran) {
+               ret = FALSE;
+               g_set_error (error,
+                            GS_PLUGIN_LOADER_ERROR,
+                            GS_PLUGIN_LOADER_ERROR_FAILED,
+                            "no plugin could handle refresh");
+               goto out;
+       }
+out:
+       return ret;
+}
+
+/**
+ * gs_plugin_loader_refresh_state_finish:
+ **/
+static void
+gs_plugin_loader_refresh_state_finish (GsPluginLoaderAsyncState *state,
+                                      const GError *error)
+{
+       if (state->ret) {
+               g_simple_async_result_set_op_res_gboolean (state->res, TRUE);
+       } else {
+               g_simple_async_result_set_from_error (state->res, error);
+       }
+
+       /* deallocate */
+       if (state->cancellable != NULL)
+               g_object_unref (state->cancellable);
+       g_object_unref (state->res);
+       g_object_unref (state->plugin_loader);
+       g_slice_free (GsPluginLoaderAsyncState, state);
+}
+
+/**
+ * gs_plugin_loader_refresh_thread_cb:
+ **/
+static void
+gs_plugin_loader_refresh_thread_cb (GSimpleAsyncResult *res,
+                                   GObject *object,
+                                   GCancellable *cancellable)
+{
+       GError *error = NULL;
+       GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+
+       state->ret = gs_plugin_loader_run_refresh (plugin_loader,
+                                                  state->cache_age,
+                                                  state->flags,
+                                                  cancellable,
+                                                  &error);
+       if (!state->ret) {
+               gs_plugin_loader_refresh_state_finish (state, error);
+               g_error_free (error);
+               goto out;
+       }
+
+       /* success */
+       gs_plugin_loader_refresh_state_finish (state, NULL);
+out:
+       return;
+}
+
+/**
+ * gs_plugin_loader_refresh_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_refine()
+ * function.
+ **/
+void
+gs_plugin_loader_refresh_async (GsPluginLoader *plugin_loader,
+                               guint cache_age,
+                               GsPluginRefreshFlags flags,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+       GCancellable *tmp;
+       GsPluginLoaderAsyncState *state;
+
+       g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+       /* save state */
+       state = g_slice_new0 (GsPluginLoaderAsyncState);
+       state->res = g_simple_async_result_new (G_OBJECT (plugin_loader),
+                                               callback,
+                                               user_data,
+                                               gs_plugin_loader_refresh_async);
+       state->plugin_loader = g_object_ref (plugin_loader);
+       state->flags = flags;
+       state->cache_age = cache_age;
+       if (cancellable != NULL)
+               state->cancellable = g_object_ref (cancellable);
+
+       /* run in a thread */
+       tmp = g_cancellable_new ();
+       g_object_set_data (G_OBJECT (tmp), "state", state);
+       g_simple_async_result_run_in_thread (G_SIMPLE_ASYNC_RESULT (state->res),
+                                            gs_plugin_loader_refresh_thread_cb,
+                                            0,
+                                            (GCancellable *) tmp);
+       g_object_unref (tmp);
+}
+
+/**
+ * gs_plugin_loader_refresh_finish:
+ *
+ * Return value: success
+ **/
+gboolean
+gs_plugin_loader_refresh_finish (GsPluginLoader *plugin_loader,
+                                GAsyncResult *res,
+                                GError **error)
+{
+       GSimpleAsyncResult *simple;
+
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), FALSE);
+       g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+       /* failed */
+       simple = G_SIMPLE_ASYNC_RESULT (res);
+       if (g_simple_async_result_propagate_error (simple, error))
+               return FALSE;
+
+       /* grab detail */
+       return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+/******************************************************************************/
+
 /* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 7e94b7e..73d7af9 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2007-2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -162,6 +162,15 @@ void                gs_plugin_loader_app_action_async      (GsPluginLoader 
*plugin_loader,
 gboolean        gs_plugin_loader_app_action_finish     (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
+gboolean        gs_plugin_loader_refresh_finish        (GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
+void            gs_plugin_loader_refresh_async         (GsPluginLoader *plugin_loader,
+                                                        guint           cache_age,
+                                                        GsPluginRefreshFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
 GsAppState      gs_plugin_loader_get_state_for_app     (GsPluginLoader *plugin_loader,
                                                         GsApp          *app);
 GPtrArray      *gs_plugin_loader_get_pending           (GsPluginLoader *plugin_loader);
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 864e3dc..3f4240f 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2012-2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2012-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -94,6 +94,11 @@ typedef enum {
        GS_PLUGIN_REFINE_FLAGS_LAST
 } GsPluginRefineFlags;
 
+typedef enum {
+       GS_PLUGIN_REFRESH_FLAGS_UPDATES                 = 1 << 0,
+       GS_PLUGIN_REFRESH_FLAGS_LAST
+} GsPluginRefreshFlags;
+
 /* helpers */
 #define        GS_PLUGIN_ERROR                                 1
 #define        GS_PLUGIN_GET_PRIVATE(x)                        g_new0 (x,1)
@@ -125,6 +130,11 @@ typedef gboolean    (*GsPluginRefineFunc)          (GsPlugin       *plugin,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+typedef gboolean        (*GsPluginRefreshFunc  )       (GsPlugin       *plugin,
+                                                        guint           cache_age,
+                                                        GsPluginRefreshFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 const gchar    *gs_plugin_get_name                     (void);
 void            gs_plugin_initialize                   (GsPlugin       *plugin);
@@ -201,6 +211,11 @@ gboolean    gs_plugin_app_set_rating               (GsPlugin       *plugin,
                                                         GsApp          *app,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_refresh                      (GsPlugin       *plugin,
+                                                        guint           cache_age,
+                                                        GsPluginRefreshFlags flags,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 G_END_DECLS
 
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 73cf115..adc4279 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -173,7 +173,7 @@ gs_shell_details_refresh (GsShellDetails *shell_details)
        }
 
        /* spinner */
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner"));
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner_end"));
        if (kind == GS_APP_KIND_SYSTEM) {
                gtk_widget_set_visible (widget, FALSE);
                gtk_spinner_stop (GTK_SPINNER (widget));
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index db938fc..21c623b 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2014 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -31,20 +31,37 @@
 #include "gs-app-widget.h"
 #include "gs-markdown.h"
 
+/* this isn't ideal, as PK should be abstracted away in a plugin, but
+ * GNetworkMonitor doesn't provide us with a connection type */
+#include <packagekit-glib2/packagekit.h>
+
 static void    gs_shell_updates_finalize       (GObject        *object);
 
 #define GS_SHELL_UPDATES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_SHELL_UPDATES, 
GsShellUpdatesPrivate))
 
+typedef enum {
+       GS_SHELL_UPDATES_STATE_STARTUP,
+       GS_SHELL_UPDATES_STATE_ACTION_REFRESH,
+       GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES,
+       GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW,
+       GS_SHELL_UPDATES_STATE_HAS_UPDATES,
+       GS_SHELL_UPDATES_STATE_FAILED,
+       GS_SHELL_UPDATES_STATE_LAST,
+} GsShellUpdatesState;
+
 struct GsShellUpdatesPrivate
 {
        GsPluginLoader          *plugin_loader;
        GtkBuilder              *builder;
        GCancellable            *cancellable;
+       GCancellable            *cancellable_refresh;
        GtkListBox              *list_box_updates;
        gboolean                 cache_valid;
-       gboolean                 waiting;
        GsShell                 *shell;
        GsApp                   *app;
+       PkControl               *control;
+       GsShellUpdatesState      state;
+       gboolean                 has_agreed_to_mobile_data;
 };
 
 enum {
@@ -66,6 +83,198 @@ gs_shell_updates_invalidate (GsShellUpdates *shell_updates)
 }
 
 /**
+ * gs_shell_updates_update_ui_state:
+ **/
+static void
+gs_shell_updates_update_ui_state (GsShellUpdates *shell_updates)
+{
+       gchar *tmp;
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+       GtkWidget *widget;
+       PkNetworkEnum network_state;
+       gboolean is_free_connection;
+
+       /* get the current network state */
+       g_object_get (priv->control, "network-state", &network_state, NULL);
+network_state = PK_NETWORK_ENUM_MOBILE;//xxx
+       switch (network_state) {
+       case PK_NETWORK_ENUM_ONLINE:
+       case PK_NETWORK_ENUM_WIFI:
+       case PK_NETWORK_ENUM_WIRED:
+               is_free_connection = TRUE;
+               break;
+       default:
+               is_free_connection = FALSE;
+               break;
+       }
+
+       /* main spinner */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "spinner_updates"));
+       switch (priv->state) {
+       case GS_SHELL_UPDATES_STATE_STARTUP:
+       case GS_SHELL_UPDATES_STATE_ACTION_REFRESH:
+       case GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES:
+               gs_start_spinner (GTK_SPINNER (widget));
+               break;
+       case GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW:
+       case GS_SHELL_UPDATES_STATE_HAS_UPDATES:
+       case GS_SHELL_UPDATES_STATE_FAILED:
+               gs_stop_spinner (GTK_SPINNER (widget));
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       /* spinner text */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_updates_spinner"));
+       switch (priv->state) {
+       case GS_SHELL_UPDATES_STATE_STARTUP:
+               tmp = g_strdup_printf ("%s\n%s",
+                                      /* TRANSLATORS: the updates panel is starting up */
+                                      _("Setting up updates…"),
+                                      _("(This could take a while)"));
+               gtk_label_set_label (GTK_LABEL (widget), tmp);
+               g_free (tmp);
+       case GS_SHELL_UPDATES_STATE_ACTION_REFRESH:
+               tmp = g_strdup_printf ("%s\n%s",
+                                      /* TRANSLATORS: the updates panel is starting up */
+                                      _("Looking for new updates…"),
+                                      _("(This could take a while)"));
+               gtk_label_set_label (GTK_LABEL (widget), tmp);
+               g_free (tmp);
+               break;
+       case GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES:
+               /* TRANSLATORS: this is when the updates panel is starting up */
+               gtk_label_set_label (GTK_LABEL (widget), _("Checking for updates…"));
+               break;
+       case GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW:
+       case GS_SHELL_UPDATES_STATE_HAS_UPDATES:
+       case GS_SHELL_UPDATES_STATE_FAILED:
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       /* headerbar spinner */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner_start"));
+       switch (priv->state) {
+       case GS_SHELL_UPDATES_STATE_ACTION_REFRESH:
+               gtk_widget_show (widget);
+               gtk_spinner_start (GTK_SPINNER (widget));
+               break;
+       case GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES:
+       case GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW:
+       case GS_SHELL_UPDATES_STATE_HAS_UPDATES:
+       case GS_SHELL_UPDATES_STATE_STARTUP:
+       case GS_SHELL_UPDATES_STATE_FAILED:
+               gtk_spinner_stop (GTK_SPINNER (widget));
+               gtk_widget_hide (widget);
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       /* headerbar refresh icon */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh_image"));
+       switch (priv->state) {
+       case GS_SHELL_UPDATES_STATE_ACTION_REFRESH:
+               gtk_image_set_from_icon_name (GTK_IMAGE (widget),
+                                             "media-playback-stop-symbolic", 0);
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+               gtk_widget_show (widget);
+               break;
+       case GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES:
+       case GS_SHELL_UPDATES_STATE_STARTUP:
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+               gtk_widget_hide (widget);
+               break;
+       case GS_SHELL_UPDATES_STATE_FAILED:
+       case GS_SHELL_UPDATES_STATE_HAS_UPDATES:
+               gtk_image_set_from_icon_name (GTK_IMAGE (widget),
+                                             "view-refresh-symbolic", 0);
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+               gtk_widget_show (widget);
+               break;
+       case GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW:
+               gtk_image_set_from_icon_name (GTK_IMAGE (widget),
+                                             "view-refresh-symbolic", 0);
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+               gtk_widget_set_visible (widget,
+                                       is_free_connection || priv->has_agreed_to_mobile_data);
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       /* stack */
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_updates"));
+       switch (priv->state) {
+       case GS_SHELL_UPDATES_STATE_STARTUP:
+       case GS_SHELL_UPDATES_STATE_ACTION_REFRESH:
+       case GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES:
+               gtk_stack_set_visible_child_name (GTK_STACK (widget), "spinner");
+               break;
+       case GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW:
+               /* check we have a "free" network connection */
+               switch (network_state) {
+               case PK_NETWORK_ENUM_ONLINE:
+               case PK_NETWORK_ENUM_WIFI:
+               case PK_NETWORK_ENUM_WIRED:
+                       gtk_stack_set_visible_child_name (GTK_STACK (widget), "uptodate");
+                       break;
+               case PK_NETWORK_ENUM_OFFLINE:
+                       gtk_stack_set_visible_child_name (GTK_STACK (widget), "offline");
+                       break;
+               case PK_NETWORK_ENUM_MOBILE:
+                       if (priv->has_agreed_to_mobile_data) {
+                               gtk_stack_set_visible_child_name (GTK_STACK (widget), "uptodate");
+                       } else {
+                               gtk_stack_set_visible_child_name (GTK_STACK (widget), "mobile");
+                       }
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case GS_SHELL_UPDATES_STATE_HAS_UPDATES:
+               gtk_stack_set_visible_child_name (GTK_STACK (widget), "view");
+               break;
+       case GS_SHELL_UPDATES_STATE_FAILED:
+               gtk_stack_set_visible_child_name (GTK_STACK (widget), "failed");
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+}
+
+/**
+ * gs_shell_updates_set_state:
+ **/
+static void
+gs_shell_updates_set_state (GsShellUpdates *shell_updates,
+                           GsShellUpdatesState state)
+{
+       shell_updates->priv->state = state;
+       gs_shell_updates_update_ui_state (shell_updates);
+}
+
+/**
+ * gs_shell_updates_notify_network_state_cb:
+ **/
+static void
+gs_shell_updates_notify_network_state_cb (PkControl *control,
+                                         GParamSpec *pspec,
+                                         GsShellUpdates *shell_updates)
+{
+       gs_shell_updates_update_ui_state (shell_updates);
+}
+
+/**
  * gs_shell_updates_get_updates_cb:
  **/
 static void
@@ -80,12 +289,8 @@ gs_shell_updates_get_updates_cb (GsPluginLoader *plugin_loader,
        GsShellUpdatesPrivate *priv = shell_updates->priv;
        GtkWidget *widget;
 
-       priv->waiting = FALSE;
        priv->cache_valid = TRUE;
 
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "spinner_updates"));
-       gs_stop_spinner (GTK_SPINNER (widget));
-
        /* get the results */
        list = gs_plugin_loader_get_updates_finish (plugin_loader, res, &error);
 
@@ -117,24 +322,21 @@ gs_shell_updates_get_updates_cb (GsPluginLoader *plugin_loader,
                                     GS_PLUGIN_LOADER_ERROR,
                                     GS_PLUGIN_LOADER_ERROR_NO_RESULTS)) {
                        g_debug ("no updates to show");
-                       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                                                    "stack_updates"));
-                       gtk_stack_set_visible_child_name (GTK_STACK (widget), "uptodate");
+                       gs_shell_updates_set_state (shell_updates,
+                                                   GS_SHELL_UPDATES_STATE_NO_UPDATES_TO_SHOW);
                } else {
                        g_warning ("failed to get updates: %s", error->message);
                        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                                                    "stack_updates"));
-                       gtk_stack_set_visible_child_name (GTK_STACK (widget), "failed");
-                       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                                     "label_updates_failed"));
                        gtk_label_set_label (GTK_LABEL (widget), error->message);
+                       gs_shell_updates_set_state (shell_updates,
+                                                   GS_SHELL_UPDATES_STATE_FAILED);
                }
                g_error_free (error);
                goto out;
        } else {
-               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                                            "stack_updates"));
-               gtk_stack_set_visible_child_name (GTK_STACK (widget), "view");
+               gs_shell_updates_set_state (shell_updates,
+                                           GS_SHELL_UPDATES_STATE_HAS_UPDATES);
        }
        for (l = list; l != NULL; l = l->next) {
                app = GS_APP (l->data);
@@ -161,7 +363,6 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates,
        GsShellUpdatesPrivate *priv = shell_updates->priv;
        GtkWidget *widget;
        GtkWindow *window;
-       GtkSpinner *spinner;
        GList *list;
        guint64 refine_flags;
 
@@ -179,6 +380,11 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates,
                gtk_window_set_title (window, _("Recent Software Updates"));
        }
 
+       if (gs_shell_get_mode (priv->shell) == GS_SHELL_MODE_UPDATES) {
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+               gtk_widget_set_visible (widget, TRUE);
+       }
+
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_updates"));
        if (scroll_up) {
                GtkAdjustment *adj;
@@ -187,7 +393,7 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates,
        }
 
        /* no need to refresh */
-       if (priv->cache_valid) {
+       if (FALSE && priv->cache_valid) {
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_updates"));
                gtk_style_context_remove_class (gtk_widget_get_style_context (widget), "needs-attention");
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_update_all"));
@@ -197,7 +403,7 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates,
                return;
        }
 
-       if (priv->waiting)
+       if (priv->state == GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES)
                return;
 
        gs_container_remove_all (GTK_CONTAINER (priv->list_box_updates));
@@ -207,18 +413,13 @@ gs_shell_updates_refresh (GsShellUpdates *shell_updates,
                       GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION;
        if (show_historical)
                refine_flags |= GS_PLUGIN_REFINE_FLAGS_USE_HISTORY;
+       gs_shell_updates_set_state (shell_updates,
+                                   GS_SHELL_UPDATES_STATE_ACTION_GET_UPDATES);
        gs_plugin_loader_get_updates_async (priv->plugin_loader,
                                            refine_flags,
                                            priv->cancellable,
                                            (GAsyncReadyCallback) gs_shell_updates_get_updates_cb,
                                            shell_updates);
-
-       spinner = GTK_SPINNER (gtk_builder_get_object (priv->builder, "spinner_updates"));
-       gs_start_spinner (spinner);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "stack_updates"));
-       gtk_stack_set_visible_child_name (GTK_STACK (widget), "spinner");
-
-       priv->waiting = TRUE;
 }
 
 /**
@@ -443,6 +644,205 @@ gs_shell_updates_button_close_cb (GtkWidget *widget, GsShellUpdates *shell_updat
 }
 
 /**
+ * gs_shell_updates_refresh_cb:
+ **/
+static void
+gs_shell_updates_refresh_cb (GsPluginLoader *plugin_loader,
+                            GAsyncResult *res,
+                            GsShellUpdates *shell_updates)
+{
+       GError *error = NULL;
+       gboolean ret;
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+       GtkWidget *widget;
+
+       /* get the results */
+       ret = gs_plugin_loader_refresh_finish (plugin_loader, res, &error);
+       if (!ret) {
+               g_warning ("failed to refresh: %s", error->message);
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                                            "label_updates_failed"));
+               gtk_label_set_label (GTK_LABEL (widget), error->message);
+               gs_shell_updates_set_state (shell_updates,
+                                           GS_SHELL_UPDATES_STATE_FAILED);
+               g_error_free (error);
+               return;
+       }
+
+       /* get the new list */
+       gs_shell_updates_refresh (shell_updates, FALSE, TRUE);
+}
+
+/**
+ * gs_shell_updates_get_new_updates:
+ **/
+static void
+gs_shell_updates_get_new_updates (GsShellUpdates *shell_updates)
+{
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+
+       /* force a check for updates and download */
+       gs_shell_updates_set_state (shell_updates,
+                                   GS_SHELL_UPDATES_STATE_ACTION_REFRESH);
+       g_cancellable_reset (priv->cancellable_refresh);
+       gs_plugin_loader_refresh_async (priv->plugin_loader,
+                                       10 * 60,
+                                       GS_PLUGIN_REFRESH_FLAGS_UPDATES,
+                                       priv->cancellable_refresh,
+                                       (GAsyncReadyCallback) gs_shell_updates_refresh_cb,
+                                       shell_updates);
+}
+
+/**
+ * gs_shell_updates_show_network_settings:
+ **/
+static void
+gs_shell_updates_show_network_settings (GsShellUpdates *shell_updates)
+{
+       gboolean ret;
+       GError *error = NULL;
+
+       ret = g_spawn_command_line_async ("gnome-control-center network", &error);
+       if (!ret) {
+               g_warning ("Failed to open the control center: %s", error->message);
+               g_error_free (error);
+       }
+}
+
+/**
+ * gs_shell_updates_refresh_confirm_cb:
+ **/
+static void
+gs_shell_updates_refresh_confirm_cb (GtkDialog *dialog,
+                                    GtkResponseType response_type,
+                                    GsShellUpdates *shell_updates)
+{
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+
+       switch (response_type) {
+       case GTK_RESPONSE_REJECT:
+               /* open the control center */
+               gs_shell_updates_show_network_settings (shell_updates);
+               break;
+       case GTK_RESPONSE_ACCEPT:
+               priv->has_agreed_to_mobile_data = TRUE;
+               gs_shell_updates_get_new_updates (shell_updates);
+               break;
+       case GTK_RESPONSE_CANCEL:
+       case GTK_RESPONSE_DELETE_EVENT:
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+/**
+ * gs_shell_updates_button_network_settings_cb:
+ **/
+static void
+gs_shell_updates_button_network_settings_cb (GtkWidget *widget,
+                                            GsShellUpdates *shell_updates)
+{
+       gs_shell_updates_show_network_settings (shell_updates);
+}
+
+/**
+ * gs_shell_updates_button_mobile_refresh_cb:
+ **/
+static void
+gs_shell_updates_button_mobile_refresh_cb (GtkWidget *widget,
+                                          GsShellUpdates *shell_updates)
+{
+       shell_updates->priv->has_agreed_to_mobile_data = TRUE;
+       gs_shell_updates_get_new_updates (shell_updates);
+}
+
+/**
+ * gs_shell_updates_button_refresh_cb:
+ **/
+static void
+gs_shell_updates_button_refresh_cb (GtkWidget *widget,
+                                   GsShellUpdates *shell_updates)
+{
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+       GtkWidget *dialog;
+       PkNetworkEnum network_state;
+
+       /* cancel existing action? */
+       if (priv->state == GS_SHELL_UPDATES_STATE_ACTION_REFRESH) {
+               g_cancellable_cancel (priv->cancellable_refresh);
+               return;
+       }
+
+       /* check we have a "free" network connection */
+       g_object_get (priv->control,
+                     "network-state", &network_state,
+                     NULL);
+       switch (network_state) {
+       case PK_NETWORK_ENUM_ONLINE:
+       case PK_NETWORK_ENUM_WIFI:
+       case PK_NETWORK_ENUM_WIRED:
+               gs_shell_updates_get_new_updates (shell_updates);
+               break;
+       case PK_NETWORK_ENUM_OFFLINE:
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_update"));
+               dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
+                                                GTK_DIALOG_MODAL |
+                                                GTK_DIALOG_DESTROY_WITH_PARENT |
+                                                GTK_DIALOG_USE_HEADER_BAR,
+                                                GTK_MESSAGE_ERROR,
+                                                GTK_BUTTONS_CANCEL,
+                                                /* TRANSLATORS: can't do updates check */
+                                                _("No Network"));
+               gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                         /* TRANSLATORS: we need network
+                                                          * to do the updates check */
+                                                         _("Internet access is required to check for 
updates."));
+               gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                      /* TRANSLATORS: this is a link to the
+                                       * control-center network panel */
+                                      _("Network Settings"),
+                                      GTK_RESPONSE_REJECT);
+               g_signal_connect (dialog, "response",
+                                 G_CALLBACK (gs_shell_updates_refresh_confirm_cb),
+                                 shell_updates);
+               gtk_window_present (GTK_WINDOW (dialog));
+               break;
+       case PK_NETWORK_ENUM_MOBILE:
+               if (priv->has_agreed_to_mobile_data) {
+                       gs_shell_updates_get_new_updates (shell_updates);
+                       break;
+               }
+               widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_update"));
+               dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
+                                                GTK_DIALOG_MODAL |
+                                                GTK_DIALOG_DESTROY_WITH_PARENT |
+                                                GTK_DIALOG_USE_HEADER_BAR,
+                                                GTK_MESSAGE_ERROR,
+                                                GTK_BUTTONS_CANCEL,
+                                                /* TRANSLATORS: $$$ */
+                                                _("Charges may apply"));
+               gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                         /* TRANSLATORS: we need network
+                                                          * to do the updates check */
+                                                         _("Checking for updates while using mobile 
broadband could cause you to incur charges."));
+               gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                      /* TRANSLATORS: this is a link to the
+                                       * control-center network panel */
+                                      _("Check Anyway"),
+                                      GTK_RESPONSE_ACCEPT);
+               g_signal_connect (dialog, "response",
+                                 G_CALLBACK (gs_shell_updates_refresh_confirm_cb),
+                                 shell_updates);
+               gtk_window_present (GTK_WINDOW (dialog));
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+/**
  * gs_shell_updates_button_back_cb:
  **/
 static void
@@ -589,14 +989,23 @@ gs_shell_updates_setup (GsShellUpdates *shell_updates,
                        GtkBuilder *builder,
                        GCancellable *cancellable)
 {
+       GError *error = NULL;
        GsShellUpdatesPrivate *priv = shell_updates->priv;
        GtkWidget *widget;
        GtkWidget *sw;
+       gboolean ret;
 
        g_return_if_fail (GS_IS_SHELL_UPDATES (shell_updates));
 
        priv->shell = shell;
 
+       /* get the network state */
+       ret = pk_control_get_properties (priv->control, cancellable, &error);
+       if (!ret) {
+               g_warning ("failed to get properties: %s", error->message);
+               g_error_free (error);
+       }
+
        priv->plugin_loader = g_object_ref (plugin_loader);
        g_signal_connect (priv->plugin_loader, "pending-apps-changed",
                          G_CALLBACK (gs_shell_updates_pending_apps_changed_cb),
@@ -635,6 +1044,18 @@ gs_shell_updates_setup (GsShellUpdates *shell_updates,
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (gs_shell_updates_button_close_cb),
                          shell_updates);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+       g_signal_connect (widget, "clicked",
+                         G_CALLBACK (gs_shell_updates_button_refresh_cb),
+                         shell_updates);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_updates_mobile"));
+       g_signal_connect (widget, "clicked",
+                         G_CALLBACK (gs_shell_updates_button_mobile_refresh_cb),
+                         shell_updates);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_updates_offline"));
+       g_signal_connect (widget, "clicked",
+                         G_CALLBACK (gs_shell_updates_button_network_settings_cb),
+                         shell_updates);
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_update"));
        g_signal_connect (widget, "delete-event",
                          G_CALLBACK (gtk_widget_hide_on_delete), shell_updates);
@@ -654,6 +1075,12 @@ gs_shell_updates_setup (GsShellUpdates *shell_updates,
        widget = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw));
        g_signal_connect (widget, "map", G_CALLBACK (scrollbar_mapped_cb), sw);
        g_signal_connect (widget, "unmap", G_CALLBACK (scrollbar_mapped_cb), sw);
+
+       g_signal_connect (shell_updates->priv->control, "notify::network-state",
+                         G_CALLBACK (gs_shell_updates_notify_network_state_cb),
+                         shell_updates);
+
+       gs_shell_updates_update_ui_state (shell_updates);
 }
 
 /**
@@ -675,6 +1102,9 @@ static void
 gs_shell_updates_init (GsShellUpdates *shell_updates)
 {
        shell_updates->priv = GS_SHELL_UPDATES_GET_PRIVATE (shell_updates);
+       shell_updates->priv->control = pk_control_new ();
+       shell_updates->priv->cancellable_refresh = g_cancellable_new ();
+       shell_updates->priv->state = GS_SHELL_UPDATES_STATE_STARTUP;
 }
 
 /**
@@ -686,9 +1116,13 @@ gs_shell_updates_finalize (GObject *object)
        GsShellUpdates *shell_updates = GS_SHELL_UPDATES (object);
        GsShellUpdatesPrivate *priv = shell_updates->priv;
 
+       g_cancellable_cancel (priv->cancellable_refresh);
+
+       g_object_unref (priv->cancellable_refresh);
        g_object_unref (priv->builder);
        g_object_unref (priv->plugin_loader);
        g_object_unref (priv->cancellable);
+       g_object_unref (priv->control);
        g_clear_object (&priv->app);
 
        G_OBJECT_CLASS (gs_shell_updates_parent_class)->finalize (object);
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 0af82cd..65192e9 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -126,7 +126,10 @@ gs_shell_change_mode (GsShell *shell,
         */
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_update_all"));
        gtk_widget_hide (widget);
-       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner"));
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner_start"));
+       gtk_spinner_stop (GTK_SPINNER (widget));
+       gtk_widget_hide (widget);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_spinner_end"));
        gtk_spinner_stop (GTK_SPINNER (widget));
        gtk_widget_hide (widget);
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "header_label"));
@@ -137,6 +140,8 @@ gs_shell_change_mode (GsShell *shell,
        gtk_widget_hide (widget);
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_select"));
        gtk_widget_hide (widget);
+       widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_refresh"));
+       gtk_widget_hide (widget);
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "application_details_header"));
        gtk_widget_hide (widget);
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_back"));
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index c82f82f..49f054d 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -41,6 +41,7 @@ plugin_LTLIBRARIES =                                  \
        libgs_plugin_systemd-updates.la                 \
        libgs_plugin_packagekit-search.la               \
        libgs_plugin_packagekit-refine.la               \
+       libgs_plugin_packagekit-refresh.la              \
        libgs_plugin_packagekit-updates.la              \
        libgs_plugin_packagekit-offline.la              \
        libgs_plugin_packagekit-history.la              \
@@ -146,6 +147,14 @@ libgs_plugin_packagekit_refine_la_LIBADD = $(GS_PLUGIN_LIBS) $(PACKAGEKIT_LIBS)
 libgs_plugin_packagekit_refine_la_LDFLAGS = -module -avoid-version
 libgs_plugin_packagekit_refine_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARNINGFLAGS_C)
 
+libgs_plugin_packagekit_refresh_la_SOURCES =           \
+       gs-plugin-packagekit-refresh.c                  \
+       packagekit-common.c                             \
+       packagekit-common.h
+libgs_plugin_packagekit_refresh_la_LIBADD = $(GS_PLUGIN_LIBS) $(PACKAGEKIT_LIBS)
+libgs_plugin_packagekit_refresh_la_LDFLAGS = -module -avoid-version
+libgs_plugin_packagekit_refresh_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARNINGFLAGS_C)
+
 libgs_plugin_packagekit_search_la_SOURCES =            \
        gs-plugin-packagekit-search.c                   \
        packagekit-common.c                             \
diff --git a/src/plugins/gs-plugin-packagekit-refresh.c b/src/plugins/gs-plugin-packagekit-refresh.c
new file mode 100644
index 0000000..866387f
--- /dev/null
+++ b/src/plugins/gs-plugin-packagekit-refresh.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#define I_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE
+#include <packagekit-glib2/packagekit.h>
+#include <glib/gi18n.h>
+
+#include <gs-plugin.h>
+
+#include "packagekit-common.h"
+
+struct GsPluginPrivate {
+       PkTask                  *task;
+};
+
+/**
+ * gs_plugin_get_name:
+ */
+const gchar *
+gs_plugin_get_name (void)
+{
+       return "packagekit-refresh";
+}
+
+/**
+ * gs_plugin_initialize:
+ */
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+       /* create private area */
+       plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
+       plugin->priv->task = pk_task_new ();
+       pk_client_set_background (PK_CLIENT (plugin->priv->task), FALSE);
+       pk_client_set_interactive (PK_CLIENT (plugin->priv->task), FALSE);
+}
+
+/**
+ * gs_plugin_get_priority:
+ */
+gdouble
+gs_plugin_get_priority (GsPlugin *plugin)
+{
+       return 1.0f;
+}
+
+/**
+ * gs_plugin_destroy:
+ */
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+       g_object_unref (plugin->priv->task);
+}
+
+/**
+ * gs_plugin_packagekit_progress_cb:
+ **/
+static void
+gs_plugin_packagekit_progress_cb (PkProgress *progress,
+                                 PkProgressType type,
+                                 gpointer user_data)
+{
+       GsPluginStatus plugin_status;
+       PkStatusEnum status;
+       GsPlugin *plugin = GS_PLUGIN (user_data);
+
+       if (type != PK_PROGRESS_TYPE_STATUS)
+               return;
+       g_object_get (progress,
+                     "status", &status,
+                     NULL);
+
+       /* profile */
+       if (status == PK_STATUS_ENUM_SETUP) {
+               gs_profile_start (plugin->profile,
+                                 "packagekit-refresh::transaction");
+       } else if (status == PK_STATUS_ENUM_FINISHED) {
+               gs_profile_stop (plugin->profile,
+                                "packagekit-refresh::transaction");
+       }
+
+       plugin_status = packagekit_status_enum_to_plugin_status (status);
+       if (plugin_status != GS_PLUGIN_STATUS_UNKNOWN)
+               gs_plugin_status_update (plugin, NULL, plugin_status);
+}
+
+/**
+ * gs_plugin_refresh:
+ */
+gboolean
+gs_plugin_refresh (GsPlugin *plugin,
+                  guint cache_age,
+                  GsPluginRefreshFlags flags,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+       gboolean ret = TRUE;
+       gchar **package_ids = NULL;
+       PkBitfield filter;
+       PkBitfield transaction_flags;
+       PkPackageSack *sack = NULL;
+       PkResults *results2 = NULL;
+       PkResults *results = NULL;
+
+       /* not us */
+       if ((flags & GS_PLUGIN_REFRESH_FLAGS_UPDATES) == 0)
+               goto out;
+
+       /* update UI as this might take some time */
+       gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_WAITING);
+
+       /* do sync call */
+       filter = pk_bitfield_value (PK_FILTER_ENUM_NONE);
+       pk_client_set_cache_age (PK_CLIENT (plugin->priv->task), cache_age);
+       results = pk_client_get_updates (PK_CLIENT (plugin->priv->task),
+                                        filter,
+                                        cancellable,
+                                        gs_plugin_packagekit_progress_cb, plugin,
+                                        error);
+       if (results == NULL) {
+               ret = FALSE;
+               goto out;
+       }
+
+       /* download all the updates */
+       sack = pk_results_get_package_sack (results);
+       if (pk_package_sack_get_size (sack) == 0)
+               goto out;
+       package_ids = pk_package_sack_get_ids (sack);
+       transaction_flags = pk_bitfield_value (PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD);
+       results2 = pk_client_update_packages (PK_CLIENT (plugin->priv->task),
+                                             transaction_flags,
+                                             package_ids,
+                                             cancellable,
+                                             gs_plugin_packagekit_progress_cb, plugin,
+                                             error);
+       if (results2 == NULL) {
+               ret = FALSE;
+               goto out;
+       }
+out:
+       g_strfreev (package_ids);
+       if (sack != NULL)
+               g_object_unref (sack);
+       if (results2 != NULL)
+               g_object_unref (results2);
+       if (results != NULL)
+               g_object_unref (results);
+       return ret;
+}


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