[gnome-software] Add a packagekit-history plugin that sets history data on applications



commit a07f2772bc8970ae4cb3d9b9e1b0c59c81f1dfe4
Author: Richard Hughes <richard hughsie com>
Date:   Wed Sep 11 14:13:27 2013 +0100

    Add a packagekit-history plugin that sets history data on applications
    
    This only adds 66ms to the GUI startup, and allows us to implement the
    list ordering-by-date, the post-offline-updates UI and the installed details view.
    
    You need PackageKit from git master before this will populate details.

 src/gs-application.c                       |    1 +
 src/plugins/Makefile.am                    |    6 +
 src/plugins/gs-plugin-packagekit-history.c |  258 ++++++++++++++++++++++++++++
 3 files changed, 265 insertions(+), 0 deletions(-)
---
diff --git a/src/gs-application.c b/src/gs-application.c
index 79d5e73..46ea64f 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -167,6 +167,7 @@ gs_application_startup (GApplication *application)
         gs_plugin_loader_set_enabled (app->plugin_loader, "local-ratings", TRUE);
         gs_plugin_loader_set_enabled (app->plugin_loader, "packagekit", TRUE);
         gs_plugin_loader_set_enabled (app->plugin_loader, "packagekit-refine", TRUE);
+        gs_plugin_loader_set_enabled (app->plugin_loader, "packagekit-history", TRUE);
         gs_plugin_loader_set_enabled (app->plugin_loader, "appstream", TRUE);
         gs_plugin_loader_set_enabled (app->plugin_loader, "desktopdb", TRUE);
         gs_plugin_loader_set_enabled (app->plugin_loader, "datadir-apps", TRUE);
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index e20b075..d575ce0 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -41,6 +41,7 @@ plugin_LTLIBRARIES =                                  \
        libgs_plugin_hardcoded-screenshots.la           \
        libgs_plugin_local-ratings.la                   \
        libgs_plugin_packagekit-refine.la               \
+       libgs_plugin_packagekit-history.la              \
        libgs_plugin_packagekit.la
 
 libgs_plugin_dummy_la_SOURCES = gs-plugin-dummy.c
@@ -108,6 +109,11 @@ 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_history_la_SOURCES = gs-plugin-packagekit-history.c
+libgs_plugin_packagekit_history_la_LIBADD = $(GS_PLUGIN_LIBS)
+libgs_plugin_packagekit_history_la_LDFLAGS = -module -avoid-version
+libgs_plugin_packagekit_history_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARNINGFLAGS_C)
+
 libgs_plugin_desktopdb_la_SOURCES = gs-plugin-desktopdb.c
 libgs_plugin_desktopdb_la_LIBADD = $(GS_PLUGIN_LIBS) $(PACKAGEKIT_LIBS)
 libgs_plugin_desktopdb_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/gs-plugin-packagekit-history.c b/src/plugins/gs-plugin-packagekit-history.c
new file mode 100644
index 0000000..32a5718
--- /dev/null
+++ b/src/plugins/gs-plugin-packagekit-history.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 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 <gs-plugin.h>
+
+struct GsPluginPrivate {
+       GDBusProxy              *proxy;
+};
+
+/**
+ * gs_plugin_get_name:
+ */
+const gchar *
+gs_plugin_get_name (void)
+{
+       return "packagekit-history";
+}
+
+/**
+ * gs_plugin_initialize:
+ */
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+       GError *error = NULL;
+
+       /* create private area */
+       plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
+       plugin->priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                            G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                                            NULL,
+                                                            "org.freedesktop.PackageKit",
+                                                            "/org/freedesktop/PackageKit",
+                                                            "org.freedesktop.PackageKit",
+                                                            NULL,
+                                                            &error);
+       if (plugin->priv->proxy == NULL) {
+               g_warning ("Failed to create proxy for PackageKit: %s", error->message);
+               g_error_free (error);
+       }
+}
+
+/**
+ * gs_plugin_get_priority:
+ */
+gdouble
+gs_plugin_get_priority (GsPlugin *plugin)
+{
+       return 200.0f;
+}
+
+/**
+ * gs_plugin_destroy:
+ */
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+       g_object_unref (plugin->priv->proxy);
+}
+
+/**
+ * gs_plugin_packagekit_refine_add_history:
+ */
+static void
+gs_plugin_packagekit_refine_add_history (GsApp *app, GVariant *dict)
+{
+       const gchar *version;
+       gboolean ret;
+       GsApp *history;
+       guint64 timestamp;
+       PkInfoEnum info_enum;
+
+       /* create new history item with same ID as parent */
+       history = gs_app_new (gs_app_get_id (app));
+       gs_app_set_kind (history, GS_APP_KIND_PACKAGE);
+       gs_app_set_name (history, gs_app_get_name (app));
+
+       /* get the installed state */
+       ret = g_variant_lookup (dict, "info", "u", &info_enum);
+       g_assert (ret);
+       switch (info_enum) {
+       case PK_INFO_ENUM_INSTALLING:
+               gs_app_set_state (history, GS_APP_STATE_INSTALLED);
+               break;
+       case PK_INFO_ENUM_REMOVING:
+               gs_app_set_state (history, GS_APP_STATE_AVAILABLE);
+               break;
+       case PK_INFO_ENUM_UPDATING:
+               gs_app_set_state (history, GS_APP_STATE_UPDATABLE);
+               break;
+       default:
+               break;
+       }
+
+       /* set the history time and date */
+       ret = g_variant_lookup (dict, "timestamp", "t", &timestamp);
+       g_assert (ret);
+       gs_app_set_install_date (history, timestamp);
+
+       /* set the history version number */
+       ret = g_variant_lookup (dict, "version", "&s", &version);
+       g_assert (ret);
+       gs_app_set_version (history, version);
+
+       /* add the package to the main application */
+       gs_app_add_history (app, history);
+       g_object_unref (history);
+
+       /* use the last event as approximation of the package timestamp */
+       gs_app_set_install_date (app, timestamp);
+}
+
+static gboolean
+gs_plugin_packagekit_refine (GsPlugin *plugin,
+                            GList *list,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       const gchar **package_names;
+       gboolean ret = TRUE;
+       GError *error_local = NULL;
+       GList *l;
+       GsApp *app;
+       guint i = 0;
+       GVariantIter iter;
+       GVariant *result;
+       GVariant *tuple = NULL;
+       GVariant *value;
+
+       /* get an array of package names */
+       package_names = g_new0 (const gchar *, g_list_length (list) + 1);
+       for (l = list; l != NULL; l = l->next) {
+               app = GS_APP (l->data);
+               package_names[i++] = gs_app_get_source (app);
+       }
+
+       g_debug ("getting history for %i packages", g_list_length (list));
+       result = g_dbus_proxy_call_sync (plugin->priv->proxy,
+                                        "GetPackageHistory",
+                                        g_variant_new ("(^asu)", package_names, 0),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        200, /* 200ms should be more than enough... */
+                                        cancellable,
+                                        &error_local);
+       if (result == NULL) {
+               if (g_error_matches (error_local,
+                                    G_DBUS_ERROR,
+                                    G_DBUS_ERROR_UNKNOWN_METHOD)) {
+                       g_debug ("No history available as PackageKit is too old: %s",
+                                error_local->message);
+                       g_error_free (error_local);
+
+                       /* just set this to something non-zero so we don't keep
+                        * trying to call GetPackageHistory */
+                       for (l = list; l != NULL; l = l->next) {
+                               app = GS_APP (l->data);
+                               gs_app_set_install_date (app, 1);
+                       }
+               } else {
+                       ret = FALSE;
+                       g_propagate_error (error, error_local);
+               }
+               goto out;
+       }
+
+       /* get any results */
+       tuple = g_variant_get_child_value (result, 0);
+       for (l = list; l != NULL; l = l->next) {
+               app = GS_APP (l->data);
+               ret = g_variant_lookup (tuple,
+                                       gs_app_get_source (app),
+                                       "@aa{sv}",
+                                       &value);
+               if (!ret) {
+                       g_debug ("no history for %s, setting timestamp nonzero",
+                                gs_app_get_source (app));
+                       gs_app_set_install_date (app, 1);
+                       continue;
+               }
+
+               /* add history for application */
+               g_variant_iter_init (&iter, value);
+               while ((value = g_variant_iter_next_value (&iter))) {
+                       gs_plugin_packagekit_refine_add_history (app, value);
+                       g_variant_unref (value);
+               }
+       }
+
+       /* success */
+       ret = TRUE;
+out:
+       g_free (package_names);
+       if (tuple != NULL)
+               g_variant_unref (tuple);
+       if (result != NULL)
+               g_variant_unref (result);
+       return ret;
+}
+
+/**
+ * gs_plugin_refine:
+ */
+gboolean
+gs_plugin_refine (GsPlugin *plugin,
+                 GList *list,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       gboolean ret = TRUE;
+       GList *l;
+       GList *packages = NULL;
+       GsApp *app;
+
+       /* add any missing history data */
+       for (l = list; l != NULL; l = l->next) {
+               app = GS_APP (l->data);
+               if (gs_app_get_source (app) == NULL)
+                       continue;
+               if (gs_app_get_install_date (app) != 0)
+                       continue;
+               packages = g_list_prepend (packages, app);
+       }
+       if (packages != NULL) {
+               ret = gs_plugin_packagekit_refine (plugin,
+                                                  packages,
+                                                  cancellable,
+                                                  error);
+               if (!ret)
+                       goto out;
+       }
+out:
+       g_list_free (packages);
+       return ret;
+}


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