[gnome-software] Use an AsIcon internally to simplify a lot of complex code



commit 0a4924dbd8ed120fbd1fec261c786217643d0860
Author: Richard Hughes <richard hughsie com>
Date:   Wed Dec 10 11:49:31 2014 +0000

    Use an AsIcon internally to simplify a lot of complex code
    
    This allows us to do three things:
    
     * Simplify the icon loading, and remove a lot of hacks and heuristics used.
    
     * Split out a plugin to download icons, which allows us to support remote icons
       for non-webapps which we'll use to good effect soon.
    
     * Simplify the epiphany web-app integration to avoid having two places to query
       application state.

 src/gs-app.c                      |  107 ++++--
 src/gs-app.h                      |    7 +-
 src/gs-plugin-loader.c            |    7 +-
 src/gs-plugin.h                   |    1 +
 src/gs-shell-details.c            |   17 +-
 src/gs-utils.c                    |   66 ----
 src/gs-utils.h                    |    4 -
 src/plugins/Makefile.am           |    6 +
 src/plugins/gs-plugin-appstream.c |   42 ++-
 src/plugins/gs-plugin-epiphany.c  |  724 ++++++++-----------------------------
 src/plugins/gs-plugin-icons.c     |  216 +++++++++++
 11 files changed, 503 insertions(+), 694 deletions(-)
---
diff --git a/src/gs-app.c b/src/gs-app.c
index 5fd2e5a..ca21192 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -56,8 +56,7 @@ struct GsAppPrivate
        gchar                   *id;
        gchar                   *name;
        GsAppQuality             name_quality;
-       gchar                   *icon;
-       gchar                   *icon_path;
+       AsIcon                  *icon;
        GPtrArray               *sources;
        GPtrArray               *source_ids;
        gchar                   *project_group;
@@ -213,10 +212,19 @@ gs_app_to_string (GsApp *app)
                                gs_app_get_kudos_percentage (app));
        if (priv->name != NULL)
                g_string_append_printf (str, "\tname:\t%s\n", priv->name);
-       if (priv->icon != NULL)
-               g_string_append_printf (str, "\ticon:\t%s\n", priv->icon);
-       if (priv->icon_path != NULL)
-               g_string_append_printf (str, "\ticon-path:\t%s\n", priv->icon_path);
+       if (priv->icon != NULL) {
+               g_string_append_printf (str, "\ticon-kind:\t%s\n",
+                                       as_icon_kind_to_string (as_icon_get_kind (priv->icon)));
+               if (as_icon_get_name (priv->icon) != NULL)
+                       g_string_append_printf (str, "\ticon-name:\t%s\n",
+                                               as_icon_get_name (priv->icon));
+               if (as_icon_get_prefix (priv->icon) != NULL)
+                       g_string_append_printf (str, "\ticon-prefix:\t%s\n",
+                                               as_icon_get_prefix (priv->icon));
+               if (as_icon_get_filename (priv->icon) != NULL)
+                       g_string_append_printf (str, "\ticon-filename:\t%s\n",
+                                               as_icon_get_filename (priv->icon));
+       }
        if (priv->version != NULL)
                g_string_append_printf (str, "\tversion:\t%s\n", priv->version);
        if (priv->version_ui != NULL)
@@ -782,7 +790,7 @@ gs_app_get_pixbuf (GsApp *app)
 /**
  * gs_app_get_icon:
  */
-const gchar *
+AsIcon *
 gs_app_get_icon (GsApp *app)
 {
        g_return_val_if_fail (GS_IS_APP (app), NULL);
@@ -793,37 +801,48 @@ gs_app_get_icon (GsApp *app)
  * gs_app_set_icon:
  */
 void
-gs_app_set_icon (GsApp *app, const gchar *icon)
+gs_app_set_icon (GsApp *app, AsIcon *icon)
 {
        g_return_if_fail (GS_IS_APP (app));
-       g_return_if_fail (icon != NULL);
 
        /* save icon */
-       g_free (app->priv->icon);
-       app->priv->icon = g_strdup (icon);
+       g_clear_object (&app->priv->icon);
+       if (icon != NULL)
+               app->priv->icon = g_object_ref (icon);
 }
 
+static GtkIconTheme *icon_theme_singleton;
+static GMutex        icon_theme_lock;
+static GHashTable   *icon_theme_paths;
+
 /**
- * gs_app_get_icon_path:
+ * icon_theme_get:
  */
-const gchar *
-gs_app_get_icon_path (GsApp *app)
+static GtkIconTheme *
+icon_theme_get (void)
 {
-       g_return_val_if_fail (GS_IS_APP (app), NULL);
-       return app->priv->icon_path;
+       if (icon_theme_singleton == NULL)
+               icon_theme_singleton = gtk_icon_theme_new ();
+
+       return icon_theme_singleton;
 }
 
 /**
- * gs_app_set_icon_path:
+ * icon_theme_add_path:
  */
-void
-gs_app_set_icon_path (GsApp *app, const gchar *icon_path)
+static void
+icon_theme_add_path (const gchar *path)
 {
-       g_return_if_fail (GS_IS_APP (app));
+       if (path == NULL)
+               return;
+
+       if (icon_theme_paths == NULL)
+               icon_theme_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
-       /* save theme path */
-       g_free (app->priv->icon_path);
-       app->priv->icon_path = g_strdup (icon_path);
+       if (!g_hash_table_contains (icon_theme_paths, path)) {
+               gtk_icon_theme_prepend_search_path (icon_theme_get (), path);
+               g_hash_table_add (icon_theme_paths, g_strdup (path));
+       }
 }
 
 /**
@@ -832,6 +851,7 @@ gs_app_set_icon_path (GsApp *app, const gchar *icon_path)
 gboolean
 gs_app_load_icon (GsApp *app, gint scale, GError **error)
 {
+       AsIcon *icon;
        GdkPixbuf *pixbuf = NULL;
        gboolean ret = TRUE;
 
@@ -839,8 +859,41 @@ gs_app_load_icon (GsApp *app, gint scale, GError **error)
        g_return_val_if_fail (app->priv->icon != NULL, FALSE);
 
        /* either load from the theme or from a file */
-       pixbuf = gs_pixbuf_load (app->priv->icon, app->priv->icon_path,
-                                64 * scale, error);
+       icon = gs_app_get_icon (app);
+       switch (as_icon_get_kind (icon)) {
+       case AS_ICON_KIND_LOCAL:
+               if (as_icon_get_filename (icon) == NULL) {
+                       g_set_error (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_FAILED,
+                                    "%s icon has no filename",
+                                    as_icon_get_name (icon));
+                       return FALSE;
+               }
+               pixbuf = gdk_pixbuf_new_from_file_at_size (as_icon_get_filename (icon),
+                                                          64 * scale,
+                                                          64 * scale,
+                                                          error);
+               break;
+       case AS_ICON_KIND_STOCK:
+               g_mutex_lock (&icon_theme_lock);
+               icon_theme_add_path (as_icon_get_prefix (icon));
+               pixbuf = gtk_icon_theme_load_icon (icon_theme_get (),
+                                                  as_icon_get_name (icon),
+                                                  64 * scale,
+                                                  GTK_ICON_LOOKUP_USE_BUILTIN |
+                                                  GTK_ICON_LOOKUP_FORCE_SIZE,
+                                                  error);
+               g_mutex_unlock (&icon_theme_lock);
+               break;
+       default:
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "%s icon cannot be loaded",
+                            as_icon_kind_to_string (as_icon_get_kind (icon)));
+               break;
+       }
        if (pixbuf == NULL) {
                ret = FALSE;
                goto out;
@@ -2045,8 +2098,6 @@ gs_app_finalize (GObject *object)
        g_free (priv->id);
        g_free (priv->name);
        g_hash_table_unref (priv->urls);
-       g_free (priv->icon);
-       g_free (priv->icon_path);
        g_free (priv->licence);
        g_free (priv->menu_path);
        g_free (priv->origin);
@@ -2069,6 +2120,8 @@ gs_app_finalize (GObject *object)
        g_hash_table_unref (priv->related_hash);
        g_ptr_array_unref (priv->related);
        g_ptr_array_unref (priv->history);
+       if (priv->icon != NULL)
+               g_object_unref (priv->icon);
        if (priv->pixbuf != NULL)
                g_object_unref (priv->pixbuf);
        if (priv->featured_pixbuf != NULL)
diff --git a/src/gs-app.h b/src/gs-app.h
index 4fddcbe..0d946c3 100644
--- a/src/gs-app.h
+++ b/src/gs-app.h
@@ -194,12 +194,9 @@ void                gs_app_set_management_plugin   (GsApp          *app,
 GdkPixbuf      *gs_app_get_pixbuf              (GsApp          *app);
 void            gs_app_set_pixbuf              (GsApp          *app,
                                                 GdkPixbuf      *pixbuf);
-const gchar    *gs_app_get_icon                (GsApp          *app);
+AsIcon         *gs_app_get_icon                (GsApp          *app);
 void            gs_app_set_icon                (GsApp          *app,
-                                                const gchar    *icon);
-const gchar    *gs_app_get_icon_path           (GsApp          *app);
-void            gs_app_set_icon_path           (GsApp          *app,
-                                                const gchar    *icon_path);
+                                                AsIcon         *icon);
 gboolean        gs_app_load_icon               (GsApp          *app,
                                                 gint            scale,
                                                 GError         **error);
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 034451f..d66fc04 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -1515,6 +1515,7 @@ gs_plugin_loader_get_featured_finish (GsPluginLoader *plugin_loader,
 static gboolean
 gs_plugin_loader_convert_unavailable_app (GsApp *app, const gchar *search)
 {
+       AsIcon *icon;
        GPtrArray *keywords;
        GString *tmp;
        const gchar *keyword;
@@ -1542,9 +1543,13 @@ gs_plugin_loader_convert_unavailable_app (GsApp *app, const gchar *search)
        gs_app_set_summary_missing (app, tmp->str);
        gs_app_set_kind (app, GS_APP_KIND_MISSING);
        gs_app_set_size (app, GS_APP_SIZE_MISSING);
-       gs_app_set_icon (app, "dialog-question-symbolic");
+       icon = as_icon_new ();
+       as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+       as_icon_set_name (icon, "dialog-question-symbolic", -1);
+       gs_app_set_icon (app, icon);
        gs_app_load_icon (app, 1, NULL);
        g_string_free (tmp, TRUE);
+       g_object_unref (icon);
        return TRUE;
 }
 
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 5c9399e..84ee6f4 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -29,6 +29,7 @@
 
 #include "gs-app.h"
 #include "gs-category.h"
+#include "gs-cleanup.h"
 #include "gs-profile.h"
 
 G_BEGIN_DECLS
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 92c942b..d95dd35 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -548,7 +548,11 @@ gs_shell_details_refresh_all (GsShellDetails *shell_details)
        /* set the icon */
        tmp = gs_app_get_metadata_item (priv->app, "DataDir::desktop-icon");
        if (tmp != NULL) {
-               pixbuf = gs_pixbuf_load (tmp, NULL, 96, &error);
+               pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                  tmp, 96,
+                                                  GTK_ICON_LOOKUP_USE_BUILTIN |
+                                                  GTK_ICON_LOOKUP_FORCE_SIZE,
+                                                  &error);
                if (pixbuf == NULL) {
                        g_warning ("Failed to load desktop icon: %s",
                                   error->message);
@@ -559,11 +563,16 @@ gs_shell_details_refresh_all (GsShellDetails *shell_details)
                pixbuf = gs_app_get_pixbuf (priv->app);
        if (pixbuf == NULL && gs_app_get_state (priv->app) == AS_APP_STATE_AVAILABLE_LOCAL) {
                if (gs_app_get_kind (priv->app) == GS_APP_KIND_SOURCE)
-                       pixbuf = gs_pixbuf_load ("x-package-repository", NULL, 96, NULL);
+                       tmp = "x-package-repository";
                else if (gs_shell_details_is_addon_id_kind (priv->app))
-                       pixbuf = gs_pixbuf_load ("application-x-addon", NULL, 96, NULL);
+                       tmp = "application-x-addon";
                else
-                       pixbuf = gs_pixbuf_load ("application-x-executable", NULL, 96, NULL);
+                       tmp = "application-x-executable";
+               pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                  tmp, 96,
+                                                  GTK_ICON_LOOKUP_USE_BUILTIN |
+                                                  GTK_ICON_LOOKUP_FORCE_SIZE,
+                                                  NULL);
        }
        if (pixbuf != NULL) {
                gs_image_set_from_pixbuf (GTK_IMAGE (priv->application_details_icon), pixbuf);
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 30fdd9c..01fa2d7 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -249,72 +249,6 @@ gs_mkdir_parent (const gchar *path, GError **error)
        return ret;
 }
 
-static GtkIconTheme *icon_theme_singleton;
-static GMutex        icon_theme_lock;
-static GHashTable   *icon_theme_paths;
-
-static GtkIconTheme *
-icon_theme_get (void)
-{
-       if (icon_theme_singleton == NULL)
-               icon_theme_singleton = gtk_icon_theme_new ();
-
-       return icon_theme_singleton;
-}
-
-static void
-icon_theme_add_path (const gchar *path)
-{
-       if (path == NULL)
-               return;
-
-       if (icon_theme_paths == NULL)
-               icon_theme_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-       if (!g_hash_table_contains (icon_theme_paths, path)) {
-               gtk_icon_theme_prepend_search_path (icon_theme_get (), path);
-               g_hash_table_add (icon_theme_paths, g_strdup (path));
-       }
-}
-
-/**
- * gs_pixbuf_load:
- **/
-GdkPixbuf *
-gs_pixbuf_load (const gchar *icon_name,
-               const gchar *icon_path,
-               guint icon_size,
-               GError **error)
-{
-       GdkPixbuf *pixbuf = NULL;
-
-       g_return_val_if_fail (icon_name != NULL, NULL);
-       g_return_val_if_fail (icon_size > 0, NULL);
-
-       if (icon_name[0] == '\0') {
-               g_set_error_literal (error,
-                                    GS_PLUGIN_ERROR,
-                                    GS_PLUGIN_ERROR_FAILED,
-                                    "Icon name not specified");
-       } else if (icon_name[0] == '/') {
-               pixbuf = gdk_pixbuf_new_from_file_at_size (icon_name,
-                                                          icon_size,
-                                                          icon_size,
-                                                          error);
-       } else {
-               g_mutex_lock (&icon_theme_lock);
-               icon_theme_add_path (icon_path);
-               pixbuf = gtk_icon_theme_load_icon (icon_theme_get (),
-                                                  icon_name,
-                                                  icon_size,
-                                                  GTK_ICON_LOOKUP_USE_BUILTIN |
-                                                  GTK_ICON_LOOKUP_FORCE_SIZE,
-                                                  error);
-               g_mutex_unlock (&icon_theme_lock);
-       }
-       return pixbuf;
-}
-
 static void
 reboot_done (GObject *source, GAsyncResult *res, gpointer data)
 {
diff --git a/src/gs-utils.h b/src/gs-utils.h
index bbc7a15..aeac877 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -45,10 +45,6 @@ guint         gs_string_replace              (GString        *string,
                                         const gchar    *replace);
 gboolean gs_mkdir_parent               (const gchar    *path,
                                         GError         **error);
-GdkPixbuf *gs_pixbuf_load              (const gchar    *icon_name,
-                                        const gchar    *icon_path,
-                                        guint           icon_size,
-                                        GError         **error);
 void     gs_reboot                      (GCallback       reboot_failed);
 
 void   gs_image_set_from_pixbuf_with_scale     (GtkImage               *image,
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 9cad3e2..de60263 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -36,6 +36,7 @@ plugin_LTLIBRARIES =                                  \
        libgs_plugin_fedora_tagger_ratings.la           \
        libgs_plugin_fedora_tagger_usage.la             \
        libgs_plugin_epiphany.la                        \
+       libgs_plugin_icons.la                           \
        libgs_plugin_systemd-updates.la                 \
        libgs_plugin_packagekit-refine.la               \
        libgs_plugin_packagekit-refresh.la              \
@@ -63,6 +64,11 @@ libgs_plugin_epiphany_la_LIBADD = $(GS_PLUGIN_LIBS) $(SOUP_LIBS)
 libgs_plugin_epiphany_la_LDFLAGS = -module -avoid-version
 libgs_plugin_epiphany_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
 
+libgs_plugin_icons_la_SOURCES = gs-plugin-icons.c
+libgs_plugin_icons_la_LIBADD = $(GS_PLUGIN_LIBS) $(SOUP_LIBS)
+libgs_plugin_icons_la_LDFLAGS = -module -avoid-version
+libgs_plugin_icons_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+
 libgs_plugin_self_test_la_SOURCES = gs-plugin-self-test.c
 libgs_plugin_self_test_la_LIBADD = $(GS_PLUGIN_LIBS)
 libgs_plugin_self_test_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 08ea755..44984a5 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -256,22 +256,51 @@ out:
 static void
 gs_plugin_refine_item_pixbuf (GsPlugin *plugin, GsApp *app, AsApp *item)
 {
-       GError *error = NULL;
        AsIcon *icon;
+       GError *error = NULL;
        gboolean ret;
+       gchar *fn = NULL;
+       gchar *path = NULL;
 
        icon = as_app_get_icon_default (item);
        switch (as_icon_get_kind (icon)) {
        case AS_ICON_KIND_REMOTE:
-               gs_app_set_icon (app, as_icon_get_name (icon));
+               gs_app_set_icon (app, icon);
+               path = g_build_filename (g_get_user_data_dir (),
+                                        "gnome-software",
+                                        "icons",
+                                        NULL);
+               fn = g_build_filename (path, as_icon_get_name (icon), NULL);
+               as_icon_set_filename (icon, fn);
+               as_icon_set_prefix (icon, path);
+               if (g_file_test (fn, G_FILE_TEST_EXISTS)) {
+                       as_icon_set_kind (icon, AS_ICON_KIND_LOCAL);
+                       ret = gs_app_load_icon (app, plugin->scale, &error);
+                       if (!ret) {
+                               g_warning ("failed to load icon %s: %s",
+                                          as_icon_get_name (icon),
+                                          error->message);
+                               g_error_free (error);
+                               goto out;
+                       }
+               }
                break;
        case AS_ICON_KIND_STOCK:
        case AS_ICON_KIND_LOCAL:
-               gs_app_set_icon (app, as_icon_get_name (icon));
+               gs_app_set_icon (app, icon);
+
+               /* does not exist, so try to find using the icon theme */
+               if (as_icon_get_kind (icon) == AS_ICON_KIND_LOCAL &&
+                   as_icon_get_filename (icon) == NULL)
+                       as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+
+               /* load */
                ret = gs_app_load_icon (app, plugin->scale, &error);
                if (!ret) {
-                       g_warning ("failed to load stock icon %s: %s",
-                                  as_icon_get_name (icon), error->message);
+                       g_warning ("failed to load %s icon %s: %s",
+                                  as_icon_kind_to_string (as_icon_get_kind (icon)),
+                                  as_icon_get_name (icon),
+                                  error->message);
                        g_error_free (error);
                        return;
                }
@@ -298,6 +327,9 @@ gs_plugin_refine_item_pixbuf (GsPlugin *plugin, GsApp *app, AsApp *item)
                g_warning ("icon kind unknown for %s", as_app_get_id (item));
                break;
        }
+out:
+       g_free (path);
+       g_free (fn);
 }
 
 /**
diff --git a/src/plugins/gs-plugin-epiphany.c b/src/plugins/gs-plugin-epiphany.c
index ad7568f..0bce057 100644
--- a/src/plugins/gs-plugin-epiphany.c
+++ b/src/plugins/gs-plugin-epiphany.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
  *
@@ -30,12 +30,6 @@
 #include <gs-plugin.h>
 #include <gs-utils.h>
 
-struct GsPluginPrivate {
-       GList                   *list;
-       SoupSession             *session;
-       gsize                    loaded;
-};
-
 /**
  * gs_plugin_get_name:
  */
@@ -51,9 +45,7 @@ gs_plugin_get_name (void)
 void
 gs_plugin_initialize (GsPlugin *plugin)
 {
-       char *epiphany;
-
-       plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
+       _cleanup_free_ gchar *epiphany = NULL;
 
        /* we can only work with epiphany */
        epiphany = g_find_program_in_path ("epiphany");
@@ -62,377 +54,85 @@ gs_plugin_initialize (GsPlugin *plugin)
                g_debug ("disabling '%s' as epiphany does not exist",
                         plugin->name);
        }
-
-       g_free (epiphany);
-}
-
-/**
- * gs_plugin_destroy:
- */
-void
-gs_plugin_destroy (GsPlugin *plugin)
-{
-       if (plugin->priv->session != NULL)
-               g_object_unref (plugin->priv->session);
-       gs_plugin_list_free (plugin->priv->list);
-}
-
-/**
- * gs_plugin_add_installed_file:
- */
-static gboolean
-gs_plugin_add_installed_file (GsPlugin *plugin,
-                             const gchar *filename,
-                             GsApp **app,
-                             GError **error)
-{
-       GKeyFile *kf;
-       gboolean no_display;
-       gboolean ret;
-       gchar *comment = NULL;
-       gchar *icon = NULL;
-       gchar *name = NULL;
-       gchar *path;
-
-       /* load keyfile */
-       path = g_build_filename (g_get_user_data_dir (),
-                                "applications",
-                                filename,
-                                NULL);
-       kf = g_key_file_new ();
-       ret = g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error);
-       if (!ret)
-               goto out;
-
-       /* check we're showing this */
-       no_display = g_key_file_get_boolean (kf,
-                                            G_KEY_FILE_DESKTOP_GROUP,
-                                            G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
-                                            NULL);
-
-       /* get name */
-       name = g_key_file_get_locale_string (kf,
-                                            G_KEY_FILE_DESKTOP_GROUP,
-                                            G_KEY_FILE_DESKTOP_KEY_NAME,
-                                            NULL,
-                                            error);
-       if (name == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-
-       /* get icon */
-       icon = g_key_file_get_locale_string (kf,
-                                            G_KEY_FILE_DESKTOP_GROUP,
-                                            G_KEY_FILE_DESKTOP_KEY_ICON,
-                                            NULL,
-                                            error);
-       if (icon == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-
-       /* get comment */
-       comment = g_key_file_get_locale_string (kf,
-                                               G_KEY_FILE_DESKTOP_GROUP,
-                                               G_KEY_FILE_DESKTOP_KEY_COMMENT,
-                                               NULL,
-                                               NULL);
-       if (comment == NULL) {
-               /* TRANSLATORS: this is when a webapp has no comment */
-               comment = g_strdup_printf (_("Web app"));
-       }
-
-       /* create application */
-       *app = gs_app_new (filename);
-       gs_app_set_name (*app, GS_APP_QUALITY_NORMAL, name);
-       gs_app_set_summary (*app, GS_APP_QUALITY_NORMAL, comment);
-       /* TRANSLATORS: this is the licence of the web-app */
-       gs_app_set_licence (*app, _("Proprietary"));
-       gs_app_set_state (*app, no_display ? AS_APP_STATE_AVAILABLE :
-                                            AS_APP_STATE_INSTALLED);
-       gs_app_set_kind (*app, GS_APP_KIND_NORMAL);
-       gs_app_set_id_kind (*app, AS_ID_KIND_WEB_APP);
-       gs_app_add_source_id (*app, path);
-       gs_app_set_icon (*app, icon);
-       ret = gs_app_load_icon (*app, plugin->scale, error);
-       if (!ret)
-               goto out;
-out:
-       g_key_file_free (kf);
-       g_free (path);
-       g_free (icon);
-       g_free (name);
-       g_free (comment);
-       return ret;
 }
 
 /**
- * gs_plugin_epiphany_load_db:
+ * _gs_app_get_id_nonfull:
  */
-static gboolean
-gs_plugin_epiphany_load_db (GsPlugin *plugin, GError **error)
+static gchar *
+_gs_app_get_id_nonfull (GsApp *app)
 {
-       GDir *dir;
-       GsApp *app = NULL;
-       const gchar *filename;
-       gboolean ret = TRUE;
-       gchar *path;
-
-       /* find any web apps */
-       path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
-       dir = g_dir_open (path, 0, error);
-       if (dir == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-       while ((filename = g_dir_read_name (dir)) != NULL) {
-               if (!g_str_has_prefix (filename, "epiphany"))
-                       continue;
-               if (!g_str_has_suffix (filename, ".desktop"))
-                       continue;
-               ret = gs_plugin_add_installed_file (plugin,
-                                                   filename,
-                                                   &app,
-                                                   error);
-               if (!ret)
-                       goto out;
-               if (app != NULL) {
-                       gs_app_set_management_plugin (app, "Epiphany");
-                       gs_plugin_add_app (&plugin->priv->list, app);
-                       g_clear_object (&app);
-               }
-       }
-out:
-       g_free (path);
-       if (dir != NULL)
-               g_dir_close (dir);
-       return ret;
-}
-
-/**
- * gs_plugin_add_installed:
- */
-gboolean
-gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
-                        GCancellable *cancellable,
-                        GError **error)
-{
-       GList *l;
-       GsApp *app;
-       gboolean ret = TRUE;
-
-       /* already loaded */
-       if (g_once_init_enter (&plugin->priv->loaded)) {
-               ret = gs_plugin_epiphany_load_db (plugin, error);
-               g_once_init_leave (&plugin->priv->loaded, TRUE);
-               if (!ret)
-                       goto out;
-       }
-
-       /* add all installed apps */
-       for (l = plugin->priv->list; l != NULL; l = l->next) {
-               app = GS_APP (l->data);
-               if (gs_app_get_state (app) != AS_APP_STATE_INSTALLED)
-                       continue;
-               gs_plugin_add_app (list, app);
-       }
-out:
-       return ret;
-}
-
-/**
- * gs_plugin_epiphany_match_app_value:
- */
-static gboolean
-gs_plugin_epiphany_match_app_value (GsApp *app, const gchar *value)
-{
-       if (strcasestr (gs_app_get_name (app), value) != NULL)
-               return TRUE;
-       if (strcasestr (gs_app_get_summary (app), value) != NULL)
-               return TRUE;
-       return FALSE;
-}
-
-/**
- * gs_plugin_epiphany_match_app:
- */
-static gboolean
-gs_plugin_epiphany_match_app (GsApp *app, gchar **values)
-{
-       gboolean matches = FALSE;
-       guint i;
-
-       /* does the GsApp match *all* search keywords */
-       for (i = 0; values[i] != NULL; i++) {
-               matches = gs_plugin_epiphany_match_app_value (app, values[i]);
-               if (!matches)
-                       break;
-       }
-       return matches;
-}
-
-/**
- * gs_plugin_add_search:
- */
-gboolean
-gs_plugin_add_search (GsPlugin *plugin,
-                     gchar **values,
-                     GList **list,
-                     GCancellable *cancellable,
-                     GError **error)
-{
-       GList *l;
-       GsApp *app;
-       gboolean ret = TRUE;
-
-       /* already loaded */
-       if (g_once_init_enter (&plugin->priv->loaded)) {
-               ret = gs_plugin_epiphany_load_db (plugin, error);
-               g_once_init_leave (&plugin->priv->loaded, TRUE);
-               if (!ret)
-                       goto out;
-       }
-
-       /* add any matching apps */
-       for (l = plugin->priv->list; l != NULL; l = l->next) {
-               if (g_cancellable_set_error_if_cancelled (cancellable, error))
-                       goto out;
-
-               app = GS_APP (l->data);
-               if (gs_plugin_epiphany_match_app (app, values))
-                       gs_plugin_add_app (list, app);
-       }
-out:
-       return ret;
-}
-
-/**
- * gs_plugin_app_set_enabled:
- */
-static gboolean
-gs_plugin_app_set_enabled (const gchar *filename, gboolean enabled, GError **error)
-{
-       GKeyFile *kf;
-       gboolean ret;
-       gchar *data = NULL;
-       gsize length;
-
-       /* load file */
-       kf = g_key_file_new ();
-       ret = g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error);
-       if (!ret)
-               goto out;
-
-       /* change value */
-       g_key_file_set_boolean (kf,
-                               G_KEY_FILE_DESKTOP_GROUP,
-                               G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
-                               !enabled);
-
-       /* save value */
-       data = g_key_file_to_data (kf, &length, error);
-       if (data == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-       ret = g_file_set_contents (filename, data, length, error);
-       if (!ret)
-               goto out;
-out:
-       g_free (data);
-       g_key_file_free (kf);
-       return ret;
+       gchar *id;
+       gchar *tmp;
+
+       id = g_strdup (gs_app_get_id (app));
+       tmp = g_strrstr (id, ".desktop");
+       if (tmp != NULL)
+               *tmp = '\0';
+       return id;
 }
 
 /**
  * gs_plugin_app_install:
  */
 gboolean
-gs_plugin_app_install (GsPlugin *plugin,
-                      GsApp *app,
-                      GCancellable *cancellable,
-                      GError **error)
+gs_plugin_app_install (GsPlugin *plugin, GsApp *app,
+                      GCancellable *cancellable, GError **error)
 {
-       const gchar *filename;
-       gboolean ret = TRUE;
-
-       /* already loaded */
-       if (g_once_init_enter (&plugin->priv->loaded)) {
-               ret = gs_plugin_epiphany_load_db (plugin, error);
-               g_once_init_leave (&plugin->priv->loaded, TRUE);
-               if (!ret)
-                       goto out;
-       }
-
-       /* only process this app if was created by this plugin */
-       if (g_strcmp0 (gs_app_get_management_plugin (app), "Epiphany") != 0)
-               goto out;
-       filename = gs_app_get_source_id_default (app);
-       if (filename == NULL)
-               goto out;
-       gs_app_set_state (app, AS_APP_STATE_INSTALLING);
-       ret = gs_plugin_app_set_enabled (filename, TRUE, error);
-       if (!ret)
-               goto out;
-       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-out:
-       return ret;
-}
-
-/**
- * gs_plugin_app_remove:
- */
-gboolean
-gs_plugin_app_remove (GsPlugin *plugin,
-                     GsApp *app,
-                     GCancellable *cancellable,
-                     GError **error)
-{
-       const gchar *filename;
+       AsIcon *icon;
        gboolean ret = TRUE;
+       gsize kf_length;
+       _cleanup_error_free_ GError *error_local = NULL;
+       _cleanup_free_ gchar *app_desktop = NULL;
+       _cleanup_free_ gchar *epi_desktop = NULL;
+       _cleanup_free_ gchar *epi_dir = NULL;
+       _cleanup_free_ gchar *epi_icon = NULL;
+       _cleanup_free_ gchar *exec = NULL;
+       _cleanup_free_ gchar *hash = NULL;
+       _cleanup_free_ gchar *id_nonfull = NULL;
+       _cleanup_free_ gchar *kf_data = NULL;
+       _cleanup_free_ gchar *wmclass = NULL;
+       _cleanup_keyfile_unref_ GKeyFile *kf = NULL;
+       _cleanup_object_unref_ GFile *symlink_desktop = NULL;
+       _cleanup_object_unref_ GFile *symlink_icon = NULL;
+
+       /* only process web apps */
+       if (gs_app_get_id_kind (app) != AS_ID_KIND_WEB_APP)
+               return TRUE;
 
-       /* already loaded */
-       if (g_once_init_enter (&plugin->priv->loaded)) {
-               ret = gs_plugin_epiphany_load_db (plugin, error);
-               g_once_init_leave (&plugin->priv->loaded, TRUE);
-               if (!ret)
-                       goto out;
+       /* create the correct directory */
+       id_nonfull = _gs_app_get_id_nonfull (app);
+       hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, gs_app_get_name (app), -1);
+       epi_dir = g_strdup_printf ("%s/epiphany/app-%s-%s",
+                                  g_get_user_config_dir (),
+                                  id_nonfull,
+                                  hash);
+       g_mkdir_with_parents (epi_dir, 0755);
+
+       /* symlink icon */
+       epi_icon = g_build_filename (epi_dir, "app-icon.png", NULL);
+       symlink_icon = g_file_new_for_path (epi_icon);
+       icon = gs_app_get_icon (app);
+       ret = g_file_make_symbolic_link (symlink_icon,
+                                        as_icon_get_filename (icon),
+                                        NULL,
+                                        &error_local);
+       if (!ret) {
+               if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+                       g_debug ("ignoring icon symlink failure: %s",
+                                error_local->message);
+               } else {
+                       g_set_error (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_FAILED,
+                                    "Can't symlink icon: %s",
+                                    error_local->message);
+                       return FALSE;
+               }
        }
 
-       /* only process this app if was created by this plugin */
-       if (g_strcmp0 (gs_app_get_management_plugin (app), "Epiphany") != 0)
-               goto out;
-
-       filename = gs_app_get_source_id_default (app);
-       if (filename == NULL)
-               goto out;
-       gs_app_set_state (app, AS_APP_STATE_REMOVING);
-       ret = gs_plugin_app_set_enabled (filename, FALSE, error);
-       if (!ret)
-               goto out;
-       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-out:
-       return ret;
-}
-
-/**
- * gs_plugin_write_file:
- */
-static gboolean
-gs_plugin_write_file (GsApp *app, const gchar *filename, GError **error)
-{
-       GKeyFile *kf;
-       const gchar *url;
-       gboolean enabled;
-       gboolean ret;
-       gchar *data;
-       gchar *exec;
-       gchar *profile;
-       gchar *wmclass;
-       gsize length;
-
+       /* add desktop file */
+       wmclass = g_strdup_printf ("%s-%s", id_nonfull, hash);
        kf = g_key_file_new ();
        g_key_file_set_string (kf,
                               G_KEY_FILE_DESKTOP_GROUP,
@@ -442,15 +142,9 @@ gs_plugin_write_file (GsApp *app, const gchar *filename, GError **error)
                               G_KEY_FILE_DESKTOP_GROUP,
                               G_KEY_FILE_DESKTOP_KEY_COMMENT,
                               gs_app_get_summary (app));
-
-       url = gs_app_get_url (app, AS_URL_KIND_HOMEPAGE);
-       wmclass = g_strdup_printf ("%s-%s",
-                                  gs_app_get_id (app),
-                                  gs_app_get_metadata_item (app, "Epiphany::hash"));
-       profile = g_strdup_printf ("%s/epiphany/app-%s",
-                                  g_get_user_config_dir (), wmclass);
-       exec = g_strdup_printf ("epiphany --application-mode "
-                               "--profile=\"%s\" %s", profile, url);
+       exec = g_strdup_printf ("epiphany --application-mode --profile=\"%s\" %s",
+                               epi_dir,
+                               gs_app_get_url (app, AS_URL_KIND_HOMEPAGE));
        g_key_file_set_string (kf,
                               G_KEY_FILE_DESKTOP_GROUP,
                               G_KEY_FILE_DESKTOP_KEY_EXEC,
@@ -463,19 +157,10 @@ gs_plugin_write_file (GsApp *app, const gchar *filename, GError **error)
                                G_KEY_FILE_DESKTOP_GROUP,
                                G_KEY_FILE_DESKTOP_KEY_TERMINAL,
                                FALSE);
-       switch (gs_app_get_state (app)) {
-       case AS_APP_STATE_INSTALLING:
-       case AS_APP_STATE_INSTALLED:
-               enabled = TRUE;
-               break;
-       default:
-               enabled = FALSE;
-               break;
-       }
        g_key_file_set_boolean (kf,
                                G_KEY_FILE_DESKTOP_GROUP,
                                G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
-                               !enabled);
+                               FALSE);
        g_key_file_set_string (kf,
                               G_KEY_FILE_DESKTOP_GROUP,
                               G_KEY_FILE_DESKTOP_KEY_TYPE,
@@ -483,135 +168,74 @@ gs_plugin_write_file (GsApp *app, const gchar *filename, GError **error)
        g_key_file_set_string (kf,
                               G_KEY_FILE_DESKTOP_GROUP,
                               G_KEY_FILE_DESKTOP_KEY_ICON,
-                              gs_app_get_icon (app));
+                              epi_icon);
        g_key_file_set_string (kf,
                               G_KEY_FILE_DESKTOP_GROUP,
                               G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS,
                               wmclass);
 
        /* save keyfile */
-       data = g_key_file_to_data (kf, &length, error);
-       if (data == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-       ret = g_file_set_contents (filename, data, length, error);
+       kf_data = g_key_file_to_data (kf, &kf_length, error);
+       if (kf_data == NULL)
+               return FALSE;
+       epi_desktop = g_strdup_printf ("%s/%s.desktop", epi_dir, wmclass);
+       if (!g_file_set_contents (epi_desktop, kf_data, kf_length, error))
+               return FALSE;
+
+       /* symlink it to somewhere the shell will notice */
+       app_desktop = g_strdup_printf ("%s/applications/%s",
+                                      g_get_user_data_dir (),
+                                      gs_app_get_id (app));
+       symlink_desktop = g_file_new_for_path (app_desktop);
+       ret = g_file_make_symbolic_link (symlink_desktop,
+                                        epi_desktop,
+                                        NULL,
+                                        error);
        if (!ret)
-               goto out;
-out:
-       g_free (data);
-       g_free (profile);
-       g_free (exec);
-       g_free (wmclass);
-       g_key_file_free (kf);
-       return ret;
-}
-
-/**
- * gs_plugin_setup_networking:
- */
-static gboolean
-gs_plugin_setup_networking (GsPlugin *plugin, GError **error)
-{
-       gboolean ret = TRUE;
-
-       /* already set up */
-       if (plugin->priv->session != NULL)
-               goto out;
+               return FALSE;
 
-       /* set up a session */
-       plugin->priv->session = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT,
-                                                                   "gnome-software",
-                                                                   SOUP_SESSION_TIMEOUT, 5000,
-                                                                   NULL);
-       if (plugin->priv->session == NULL) {
-               ret = FALSE;
-               g_set_error (error,
-                            GS_PLUGIN_ERROR,
-                            GS_PLUGIN_ERROR_FAILED,
-                            "%s: failed to setup networking",
-                            plugin->name);
-               goto out;
-       }
-       soup_session_add_feature_by_type (plugin->priv->session,
-                                         SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
-out:
-       return ret;
+       /* update state */
+       gs_app_set_state (app, AS_APP_STATE_INSTALLING);
+       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       return TRUE;
 }
 
 /**
- * gs_plugin_epiphany_download:
+ * gs_plugin_app_remove:
  */
-static gboolean
-gs_plugin_epiphany_download (GsPlugin *plugin, const gchar *uri, const gchar *filename, GError **error)
+gboolean
+gs_plugin_app_remove (GsPlugin *plugin, GsApp *app,
+                     GCancellable *cancellable, GError **error)
 {
-       GInputStream *stream = NULL;
-       GdkPixbuf *pixbuf = NULL;
-       GdkPixbuf *pixbuf_new = NULL;
-       SoupMessage *msg = NULL;
-       gboolean ret = TRUE;
-       guint status_code;
-
-       /* create the GET data */
-       msg = soup_message_new (SOUP_METHOD_GET, uri);
-       if (msg == NULL) {
-               ret = FALSE;
-               g_set_error (error,
-                            GS_PLUGIN_ERROR,
-                            GS_PLUGIN_ERROR_FAILED,
-                            "%s is not a valid URL", uri);
-               goto out;
-       }
+       const gchar *epi_desktop;
+       _cleanup_free_ gchar *basename = NULL;
+       _cleanup_free_ gchar *app_desktop = NULL;
+       _cleanup_object_unref_ GFile *file_epi = NULL;
+       _cleanup_object_unref_ GFile *file_app = NULL;
 
-       /* ensure networking is set up */
-       ret = gs_plugin_setup_networking (plugin, error);
-       if (!ret)
-               goto out;
-
-       /* set sync request */
-       status_code = soup_session_send_message (plugin->priv->session, msg);
-       if (status_code != SOUP_STATUS_OK) {
-               ret = FALSE;
-               g_set_error (error,
-                            GS_PLUGIN_ERROR,
-                            GS_PLUGIN_ERROR_FAILED,
-                            "Failed to download icon %s: %s",
-                            uri, soup_status_get_phrase (status_code));
-               goto out;
-       }
-
-       /* we're assuming this is a 64x64 png file, resize if not */
-       stream = g_memory_input_stream_new_from_data (msg->response_body->data,
-                                                     msg->response_body->length,
-                                                     NULL);
-       pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
-       if (pixbuf == NULL) {
-               ret = FALSE;
-               goto out;
-       }
-       if (gdk_pixbuf_get_height (pixbuf) == 64 &&
-           gdk_pixbuf_get_width (pixbuf) == 64) {
-               pixbuf_new = g_object_ref (pixbuf);
-       } else {
-               pixbuf_new = gdk_pixbuf_scale_simple (pixbuf, 64, 64,
-                                                     GDK_INTERP_BILINEAR);
-       }
-
-       /* write file */
-       ret = gdk_pixbuf_save (pixbuf_new, filename, "png", error, NULL);
-       if (!ret)
-               goto out;
+       /* only process this app if was created by this plugin */
+       if (g_strcmp0 (gs_app_get_management_plugin (app), "Epiphany") != 0)
+               return TRUE;
+       epi_desktop = gs_app_get_source_id_default (app);
+       if (epi_desktop == NULL)
+               return TRUE;
 
-out:
-       if (stream != NULL)
-               g_object_unref (stream);
-       if (pixbuf_new != NULL)
-               g_object_unref (pixbuf_new);
-       if (pixbuf != NULL)
-               g_object_unref (pixbuf);
-       if (msg != NULL)
-               g_object_unref (msg);
-       return ret;
+       /* remove the epi 'config' file */
+       gs_app_set_state (app, AS_APP_STATE_REMOVING);
+       file_epi = g_file_new_for_path (epi_desktop);
+       if (!g_file_delete (file_epi, NULL, error))
+               return FALSE;
+
+       /* remove the shared desktop file */
+       basename = g_file_get_basename (file_epi);
+       app_desktop = g_strdup_printf ("%s/applications/%s",
+                                  g_get_user_data_dir (),
+                                  gs_app_get_id (app));
+       file_app = g_file_new_for_path (app_desktop);
+       if (!g_file_delete (file_app, NULL, error))
+               return FALSE;
+       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+       return TRUE;
 }
 
 /**
@@ -620,79 +244,26 @@ out:
 static gboolean
 gs_plugin_refine_app (GsPlugin *plugin, GsApp *app, GError **error)
 {
-       gboolean ret;
-       gchar *path = NULL;
-       gchar *filename_icon = NULL;
-       gchar *hash;
-       GError *error_local = NULL;
-       GFile *file = NULL;
+       _cleanup_free_ gchar *fn = NULL;
+       _cleanup_free_ gchar *hash = NULL;
+       _cleanup_free_ gchar *id_nonfull = NULL;
 
-       /* this is not yet installed */
-       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-
-       /* calculate SHA1 hash of name */
+       id_nonfull = _gs_app_get_id_nonfull (app);
        hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, gs_app_get_name (app), -1);
-       gs_app_set_metadata (app, "Epiphany::hash", hash);
-
-       /* download icon if it does not exist */
-       filename_icon = g_strdup_printf ("%s/epiphany/app-%s-%s/app-icon.png",
-                                        g_get_user_config_dir (),
-                                        gs_app_get_id (app),
-                                        hash);
-       if (!g_file_test (filename_icon, G_FILE_TEST_EXISTS)) {
-               ret = gs_mkdir_parent (filename_icon, error);
-               if (!ret)
-                       goto out;
-               if (g_str_has_prefix (gs_app_get_icon (app), "/")) {
-                       file = g_file_new_for_path (filename_icon);
-                       ret = g_file_make_symbolic_link (file,
-                                                        gs_app_get_icon (app),
-                                                        NULL,
-                                                        &error_local);
-               } else {
-                       ret = gs_plugin_epiphany_download (plugin,
-                                                          gs_app_get_icon (app),
-                                                          filename_icon,
-                                                          &error_local);
-               }
-               if (!ret) {
-                       /* this isn't a fatal error */
-                       gs_app_set_state (app, AS_APP_STATE_UNKNOWN);
-                       gs_app_set_state (app, AS_APP_STATE_UNAVAILABLE);
-                       g_debug ("Failed to download %s: %s",
-                                gs_app_get_icon (app), error_local->message);
-                       g_error_free (error_local);
-                       ret = TRUE;
-                       goto out;
-               }
+       fn = g_strdup_printf ("%s/epiphany/app-%s-%s/%s-%s.desktop",
+                             g_get_user_config_dir (),
+                             id_nonfull,
+                             hash,
+                             id_nonfull,
+                             hash);
+       if (g_file_test (fn, G_FILE_TEST_EXISTS)) {
+               gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+               gs_app_add_source_id (app, fn);
+               gs_app_set_management_plugin (app, "Epiphany");
+               return TRUE;
        }
-
-       /* set local icon name */
-       gs_app_set_icon (app, filename_icon);
-       ret = gs_app_load_icon (app, plugin->scale, error);
-       if (!ret)
-               goto out;
-
-       /* save file */
-       path = g_build_filename (g_get_user_data_dir (),
-                                "applications",
-                                gs_app_get_id (app),
-                                NULL);
-       ret = gs_plugin_write_file (app, path, error);
-       if (!ret)
-               goto out;
-       gs_app_add_source_id (app, path);
-
-       /* we now know about this */
-       gs_plugin_add_app (&plugin->priv->list, app);
-       gs_app_set_management_plugin (app, "Epiphany");
-out:
-       if (file != NULL)
-               g_object_unref (file);
-       g_free (hash);
-       g_free (path);
-       g_free (filename_icon);
-       return ret;
+       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+       return TRUE;
 }
 
 /**
@@ -708,15 +279,6 @@ gs_plugin_refine (GsPlugin *plugin,
        GList *l;
        GsApp *app;
        const gchar *tmp;
-       gboolean ret = TRUE;
-
-       /* already loaded */
-       if (g_once_init_enter (&plugin->priv->loaded)) {
-               ret = gs_plugin_epiphany_load_db (plugin, error);
-               g_once_init_leave (&plugin->priv->loaded, TRUE);
-               if (!ret)
-                       goto out;
-       }
 
        for (l = *list; l != NULL; l = l->next) {
                app = GS_APP (l->data);
@@ -726,10 +288,8 @@ gs_plugin_refine (GsPlugin *plugin,
                tmp = gs_app_get_source_id_default (app);
                if (tmp != NULL)
                        continue;
-               ret = gs_plugin_refine_app (plugin, app, error);
-               if (!ret)
-                       goto out;
+               if (!gs_plugin_refine_app (plugin, app, error))
+                       return FALSE;
        }
-out:
-       return ret;
+       return TRUE;
 }
diff --git a/src/plugins/gs-plugin-icons.c b/src/plugins/gs-plugin-icons.c
new file mode 100644
index 0000000..a32ed61
--- /dev/null
+++ b/src/plugins/gs-plugin-icons.c
@@ -0,0 +1,216 @@
+/* -*- 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 _GNU_SOURCE
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <libsoup/soup.h>
+
+#include <gs-plugin.h>
+#include <gs-utils.h>
+
+struct GsPluginPrivate {
+       SoupSession             *session;
+       gsize                    loaded;
+};
+
+/**
+ * gs_plugin_get_name:
+ */
+const gchar *
+gs_plugin_get_name (void)
+{
+       return "icons";
+}
+
+/**
+ * gs_plugin_initialize:
+ */
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+       plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
+}
+
+/**
+ * gs_plugin_get_deps:
+ */
+const gchar **
+gs_plugin_get_deps (GsPlugin *plugin)
+{
+       static const gchar *deps[] = {
+               "appstream",            /* needs remote icons downloaded */
+               "epiphany",             /* "" */
+               NULL };
+       return deps;
+}
+
+/**
+ * gs_plugin_destroy:
+ */
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+       if (plugin->priv->session != NULL)
+               g_object_unref (plugin->priv->session);
+}
+
+/**
+ * gs_plugin_setup_networking:
+ */
+static gboolean
+gs_plugin_setup_networking (GsPlugin *plugin, GError **error)
+{
+       /* already set up */
+       if (plugin->priv->session != NULL)
+               return TRUE;
+
+       /* set up a session */
+       plugin->priv->session = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT,
+                                                                   "gnome-software",
+                                                                   SOUP_SESSION_TIMEOUT, 5000,
+                                                                   NULL);
+       if (plugin->priv->session == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "%s: failed to setup networking",
+                            plugin->name);
+               return FALSE;
+       }
+       soup_session_add_feature_by_type (plugin->priv->session,
+                                         SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
+       return TRUE;
+}
+
+/**
+ * gs_plugin_icons_download:
+ */
+static gboolean
+gs_plugin_icons_download (GsPlugin *plugin, const gchar *uri, const gchar *filename, GError **error)
+{
+       guint status_code;
+       _cleanup_object_unref_ GdkPixbuf *pixbuf_new = NULL;
+       _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL;
+       _cleanup_object_unref_ GInputStream *stream = NULL;
+       _cleanup_object_unref_ SoupMessage *msg = NULL;
+
+       /* create the GET data */
+       msg = soup_message_new (SOUP_METHOD_GET, uri);
+       if (msg == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "%s is not a valid URL", uri);
+               return FALSE;
+       }
+
+       /* ensure networking is set up */
+       if (!gs_plugin_setup_networking (plugin, error))
+               return FALSE;
+
+       /* set sync request */
+       status_code = soup_session_send_message (plugin->priv->session, msg);
+       if (status_code != SOUP_STATUS_OK) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "Failed to download icon %s: %s",
+                            uri, soup_status_get_phrase (status_code));
+               return FALSE;
+       }
+
+       /* we're assuming this is a 64x64 png file, resize if not */
+       stream = g_memory_input_stream_new_from_data (msg->response_body->data,
+                                                     msg->response_body->length,
+                                                     NULL);
+       pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
+       if (pixbuf == NULL)
+               return FALSE;
+       if (gdk_pixbuf_get_height (pixbuf) == 64 &&
+           gdk_pixbuf_get_width (pixbuf) == 64) {
+               pixbuf_new = g_object_ref (pixbuf);
+       } else {
+               pixbuf_new = gdk_pixbuf_scale_simple (pixbuf, 64, 64,
+                                                     GDK_INTERP_BILINEAR);
+       }
+
+       /* write file */
+       return gdk_pixbuf_save (pixbuf_new, filename, "png", error, NULL);
+}
+
+/**
+ * gs_plugin_refine:
+ */
+static gboolean
+gs_plugin_refine_app (GsPlugin *plugin, GsApp *app, GError **error)
+{
+       AsIcon *ic;
+
+       /* not applicable */
+       ic = gs_app_get_icon (app);
+       if (as_icon_get_url (ic) == NULL)
+               return TRUE;
+       if (as_icon_get_filename (ic) == NULL)
+               return TRUE;
+
+       /* create runtime dir and download */
+       if (!gs_mkdir_parent (as_icon_get_prefix (ic), error))
+               return FALSE;
+       if (!gs_plugin_icons_download (plugin,
+                                      as_icon_get_url (ic),
+                                      as_icon_get_filename (ic),
+                                      error))
+               return FALSE;
+       as_icon_set_kind (ic, AS_ICON_KIND_LOCAL);
+       return gs_app_load_icon (app, plugin->scale, error);
+}
+
+/**
+ * gs_plugin_refine:
+ */
+gboolean
+gs_plugin_refine (GsPlugin *plugin,
+                 GList **list,
+                 GsPluginRefineFlags flags,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       GError *error_local = NULL;
+       GList *l;
+       GsApp *app;
+
+       for (l = *list; l != NULL; l = l->next) {
+               app = GS_APP (l->data);
+               if (gs_app_get_pixbuf (app) != NULL)
+                       continue;
+               if (gs_app_get_icon (app) == NULL)
+                       continue;
+               if (!gs_plugin_refine_app (plugin, app, &error_local)) {
+                       g_warning ("ignoring: %s", error_local->message);
+                       g_clear_error (&error_local);
+               }
+       }
+       return TRUE;
+}



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