[gnome-software] Add a plugin function to get the list of recent releases



commit 476c45710ec92e70c1ba38a8044f03c01409371d
Author: Richard Hughes <richard hughsie com>
Date:   Mon May 15 15:16:10 2017 +0100

    Add a plugin function to get the list of recent releases

 lib/gs-cmd.c                       |   17 +++++-
 lib/gs-plugin-loader-sync.c        |   48 +++++++++++++-
 lib/gs-plugin-loader-sync.h        |    8 ++-
 lib/gs-plugin-loader.c             |  129 +++++++++++++++++++++++++++++++++++-
 lib/gs-plugin-loader.h             |   12 +++-
 lib/gs-plugin-types.h              |    4 +-
 lib/gs-plugin-vfuncs.h             |   21 ++++++-
 lib/gs-plugin.c                    |    2 +
 plugins/core/gs-appstream.c        |   50 ++++++++++++++-
 plugins/core/gs-appstream.h        |    8 ++-
 plugins/core/gs-plugin-appstream.c |   12 ++++
 plugins/dummy/gs-plugin-dummy.c    |   20 ++++++
 12 files changed, 322 insertions(+), 9 deletions(-)
---
diff --git a/lib/gs-cmd.c b/lib/gs-cmd.c
index 3477ade..3f54b94 100644
--- a/lib/gs-cmd.c
+++ b/lib/gs-cmd.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -449,6 +449,21 @@ main (int argc, char **argv)
                                break;
                        }
                }
+       } else if (argc == 2 && g_strcmp0 (argv[1], "recent") == 0) {
+               for (i = 0; i < repeat; i++) {
+                       if (list != NULL)
+                               g_object_unref (list);
+                       list = gs_plugin_loader_get_recent (plugin_loader,
+                                                           60 * 60 * 24 * 60,
+                                                           refine_flags,
+                                                           GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                                           NULL,
+                                                           &error);
+                       if (list == NULL) {
+                               ret = FALSE;
+                               break;
+                       }
+               }
        } else if (argc == 2 && g_strcmp0 (argv[1], "get-categories") == 0) {
                for (i = 0; i < repeat; i++) {
                        if (categories != NULL)
diff --git a/lib/gs-plugin-loader-sync.c b/lib/gs-plugin-loader-sync.c
index 5b178d0..d7f4e6e 100644
--- a/lib/gs-plugin-loader-sync.c
+++ b/lib/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-2015 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2012-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -441,6 +441,52 @@ gs_plugin_loader_get_category_apps (GsPluginLoader *plugin_loader,
 }
 
 static void
+_get_recent_finish_sync (GsPluginLoader *plugin_loader,
+                        GAsyncResult *res,
+                        GsPluginLoaderHelper *helper)
+{
+       helper->list = gs_plugin_loader_get_recent_finish (plugin_loader,
+                                                          res,
+                                                          helper->error);
+       g_main_loop_quit (helper->loop);
+}
+
+GsAppList *
+gs_plugin_loader_get_recent (GsPluginLoader *plugin_loader,
+                            guint64 age,
+                            GsPluginRefineFlags refine_flags,
+                            GsPluginFailureFlags failure_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_get_recent_async (plugin_loader,
+                                          age,
+                                          refine_flags,
+                                          failure_flags,
+                                          cancellable,
+                                          (GAsyncReadyCallback) _get_recent_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.list;
+}
+
+static void
 gs_plugin_loader_app_refine_finish_sync (GsPluginLoader *plugin_loader,
                                         GAsyncResult *res,
                                         GsPluginLoaderHelper *helper)
diff --git a/lib/gs-plugin-loader-sync.h b/lib/gs-plugin-loader-sync.h
index 61ad653..b52f42c 100644
--- a/lib/gs-plugin-loader-sync.h
+++ b/lib/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-2015 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -78,6 +78,12 @@ GsAppList    *gs_plugin_loader_get_category_apps     (GsPluginLoader *plugin_loader,
                                                         GsPluginFailureFlags failure_flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+GsAppList      *gs_plugin_loader_get_recent            (GsPluginLoader *plugin_loader,
+                                                        guint64         age,
+                                                        GsPluginRefineFlags refine_flags,
+                                                        GsPluginFailureFlags failure_flags,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 gboolean        gs_plugin_loader_app_refine            (GsPluginLoader *plugin_loader,
                                                         GsApp          *app,
                                                         GsPluginRefineFlags refine_flags,
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 625e0e7..91a17df 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2007-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -113,6 +113,11 @@ typedef gboolean    (*GsPluginCategoryFunc)        (GsPlugin       *plugin,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+typedef gboolean        (*GsPluginGetRecentFunc)       (GsPlugin       *plugin,
+                                                        GsAppList      *list,
+                                                        guint64         age,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 typedef gboolean        (*GsPluginResultsFunc)         (GsPlugin       *plugin,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
@@ -193,6 +198,7 @@ typedef struct {
        GsPluginAction                   action;
        gboolean                         anything_ran;
        guint                            max_results;
+       guint64                          age;
        GsAppListSortFunc                sort_func;
        gpointer                         sort_func_data;
 } GsPluginLoaderJob;
@@ -659,6 +665,13 @@ gs_plugin_loader_call_vfunc (GsPluginLoaderJob *job,
                                           cancellable, &error_local);
                }
                break;
+       case GS_PLUGIN_ACTION_GET_RECENT:
+               {
+                       GsPluginGetRecentFunc plugin_func = func;
+                       ret = plugin_func (plugin, list, job->age,
+                                          cancellable, &error_local);
+               }
+               break;
        case GS_PLUGIN_ACTION_GET_UPDATES:
        case GS_PLUGIN_ACTION_GET_DISTRO_UPDATES:
        case GS_PLUGIN_ACTION_GET_UNVOTED_REVIEWS:
@@ -2650,6 +2663,120 @@ gs_plugin_loader_get_category_apps_finish (GsPluginLoader *plugin_loader,
 /******************************************************************************/
 
 static void
+gs_plugin_loader_get_recent_thread_cb (GTask *task,
+                                      gpointer object,
+                                      gpointer task_data,
+                                      GCancellable *cancellable)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       GError *error = NULL;
+       GsPluginLoaderJob *job = (GsPluginLoaderJob *) task_data;
+       const guint max_results = 20;
+
+       /* run each plugin */
+       for (guint i = 0; i < priv->plugins->len; i++) {
+               GsPlugin *plugin = g_ptr_array_index (priv->plugins, i);
+               if (g_task_return_error_if_cancelled (task))
+                       return;
+               if (!gs_plugin_loader_call_vfunc (job, plugin, NULL, NULL,
+                                                 cancellable, &error)) {
+                       g_task_return_error (task, error);
+                       return;
+               }
+       }
+
+       /* limit: TODO, use sort func */
+       if (gs_app_list_length (job->list) > max_results) {
+               gs_app_list_randomize (job->list);
+               g_debug ("truncating results to %u from %u",
+                        max_results, gs_app_list_length (job->list));
+               gs_app_list_truncate (job->list, max_results);
+       }
+
+       /* run refine() on each one */
+       if (!gs_plugin_loader_run_refine (job, job->list, cancellable, &error)) {
+               g_task_return_error (task, error);
+               return;
+       }
+
+       /* filter package list */
+       gs_app_list_filter (job->list, gs_plugin_loader_app_is_non_compulsory, NULL);
+       gs_app_list_filter (job->list, gs_plugin_loader_app_is_valid, job);
+       gs_app_list_filter (job->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
+       gs_app_list_filter (job->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
+
+       /* filter duplicates with priority */
+       gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
+       gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
+
+       /* sort, just in case the UI doesn't do this */
+       gs_app_list_sort (job->list, gs_plugin_loader_app_sort_name_cb, NULL);
+
+       /* success */
+       g_task_return_pointer (task, g_object_ref (job->list), (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * gs_plugin_loader_get_recent_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_add_recent()
+ * function. The plugins return applications that have has upstream releases
+ * within the duration of @age;
+ **/
+void
+gs_plugin_loader_get_recent_async (GsPluginLoader *plugin_loader,
+                                  guint64 age,
+                                  GsPluginRefineFlags refine_flags,
+                                  GsPluginFailureFlags failure_flags,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+       GsPluginLoaderJob *job;
+       g_autoptr(GTask) task = NULL;
+
+       g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+       /* save job */
+       job = gs_plugin_loader_job_new (plugin_loader);
+       job->refine_flags = refine_flags;
+       job->failure_flags = failure_flags;
+       job->list = gs_app_list_new ();
+       job->age = age;
+       job->action = GS_PLUGIN_ACTION_GET_RECENT;
+       job->function_name = "gs_plugin_add_recent";
+       gs_plugin_loader_job_debug (job);
+
+       /* run in a thread */
+       task = g_task_new (plugin_loader, cancellable, callback, user_data);
+       g_task_set_task_data (task, job, (GDestroyNotify) gs_plugin_loader_job_free);
+       g_task_run_in_thread (task, gs_plugin_loader_get_recent_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_get_recent_finish:
+ *
+ * Return value: (element-type GsApp) (transfer full): A list of applications
+ **/
+GsAppList *
+gs_plugin_loader_get_recent_finish (GsPluginLoader *plugin_loader,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+       g_return_val_if_fail (G_IS_TASK (res), NULL);
+       g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       gs_utils_error_convert_gio (error);
+       return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+/******************************************************************************/
+
+static void
 gs_plugin_loader_app_refine_thread_cb (GTask *task,
                                       gpointer object,
                                       gpointer task_data,
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index 745df87..c3820f2 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2007-2015 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2007-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -134,6 +134,16 @@ void                gs_plugin_loader_get_category_apps_async (GsPluginLoader       
*plugin_loader,
 GsAppList      *gs_plugin_loader_get_category_apps_finish (GsPluginLoader      *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
+void            gs_plugin_loader_get_recent_async      (GsPluginLoader *plugin_loader,
+                                                        guint64         age,
+                                                        GsPluginRefineFlags refine_flags,
+                                                        GsPluginFailureFlags failure_flags,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
+GsAppList      *gs_plugin_loader_get_recent_finish     (GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
 void            gs_plugin_loader_search_async          (GsPluginLoader *plugin_loader,
                                                         const gchar    *value,
                                                         guint           max_results,
diff --git a/lib/gs-plugin-types.h b/lib/gs-plugin-types.h
index e0519f3..077a38e 100644
--- a/lib/gs-plugin-types.h
+++ b/lib/gs-plugin-types.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2012-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2012-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -262,6 +262,7 @@ typedef enum {
  * @GS_PLUGIN_ACTION_AUTH_REGISTER:            Authentication register action
  * @GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD:       Authentication lost password action
  * @GS_PLUGIN_ACTION_URL_TO_APP:               Convert the file to an application
+ * @GS_PLUGIN_ACTION_GET_RECENT:               Get the apps recently released
  *
  * The plugin action.
  **/
@@ -304,6 +305,7 @@ typedef enum {
        GS_PLUGIN_ACTION_AUTH_REGISTER,
        GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD,
        GS_PLUGIN_ACTION_URL_TO_APP,
+       GS_PLUGIN_ACTION_GET_RECENT,
        /*< private >*/
        GS_PLUGIN_ACTION_LAST
 } GsPluginAction;
diff --git a/lib/gs-plugin-vfuncs.h b/lib/gs-plugin-vfuncs.h
index ae6a29b..4de2449 100644
--- a/lib/gs-plugin-vfuncs.h
+++ b/lib/gs-plugin-vfuncs.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2012-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2012-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -324,6 +324,25 @@ gboolean    gs_plugin_add_category_apps            (GsPlugin       *plugin,
                                                         GError         **error);
 
 /**
+ * gs_plugin_add_recent:
+ * @plugin: a #GsPlugin
+ * @age: a number of seconds
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Return all the applications that have had upstream releases recently.
+ *
+ * Plugins are expected to add new apps using gs_app_list_add().
+ *
+ * Returns: %TRUE for success or if not relevant
+ **/
+gboolean        gs_plugin_add_recent                   (GsPlugin       *plugin,
+                                                        GsAppList      *list,
+                                                        guint64         age,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
+
+/**
  * gs_plugin_add_popular:
  * @plugin: a #GsPlugin
  * @list: a #GsAppList
diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c
index 0f636a1..55e999b 100644
--- a/lib/gs-plugin.c
+++ b/lib/gs-plugin.c
@@ -1750,6 +1750,8 @@ gs_plugin_action_to_string (GsPluginAction action)
                return "auth-register";
        if (action == GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD)
                return "auth-lost-password";
+       if (action == GS_PLUGIN_ACTION_GET_RECENT)
+               return "get-recent";
        return NULL;
 }
 
diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c
index 33df85b..033df31 100644
--- a/plugins/core/gs-appstream.c
+++ b/plugins/core/gs-appstream.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2015-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -1092,6 +1092,54 @@ gs_appstream_add_popular (GsPlugin *plugin,
        return TRUE;
 }
 
+static gboolean
+_as_app_is_recent (AsApp *app, guint64 age)
+{
+       AsRelease *rel;
+       guint64 ts;
+       guint64 now;
+
+       rel = as_app_get_release_default (app);
+       if (rel == NULL)
+               return FALSE;
+       ts = as_release_get_timestamp (rel);
+       if (ts == 0)
+               return FALSE;
+       now = (guint64) g_get_real_time () / G_USEC_PER_SEC;
+       return (now - ts) < age;
+}
+
+gboolean
+gs_appstream_add_recent (GsPlugin *plugin,
+                        AsStore *store,
+                        GsAppList *list,
+                        guint64 age,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       GPtrArray *array;
+       g_autoptr(AsProfileTask) ptask = NULL;
+
+       /* find out how many packages are in each category */
+       ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
+                                         "appstream::add-recent");
+       g_assert (ptask != NULL);
+       array = as_store_get_apps (store);
+       for (guint i = 0; i < array->len; i++) {
+               g_autoptr(GsApp) app = NULL;
+               AsApp *item = g_ptr_array_index (array, i);
+               if (as_app_get_id (item) == NULL)
+                       continue;
+               if (!_as_app_is_recent (item, age))
+                       continue;
+               app = gs_appstream_create_app (plugin, item, error);
+               if (app == NULL)
+                       return FALSE;
+               gs_app_list_add (list, app);
+       }
+       return TRUE;
+}
+
 gboolean
 gs_appstream_add_featured (GsPlugin *plugin,
                           AsStore *store,
diff --git a/plugins/core/gs-appstream.h b/plugins/core/gs-appstream.h
index c4c6d98..dab26d5 100644
--- a/plugins/core/gs-appstream.h
+++ b/plugins/core/gs-appstream.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2015-2016 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2015-2017 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -63,6 +63,12 @@ gboolean      gs_appstream_add_featured              (GsPlugin       *plugin,
                                                         GsAppList      *list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_appstream_add_recent                (GsPlugin       *plugin,
+                                                        AsStore        *store,
+                                                        GsAppList      *list,
+                                                        guint64         age,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 void            gs_appstream_add_extra_info            (GsPlugin       *plugin,
                                                         AsApp          *app);
 
diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c
index 14f2865..9ce8069 100644
--- a/plugins/core/gs-plugin-appstream.c
+++ b/plugins/core/gs-plugin-appstream.c
@@ -646,6 +646,18 @@ gs_plugin_add_featured (GsPlugin *plugin,
                                          error);
 }
 
+gboolean
+gs_plugin_add_recent (GsPlugin *plugin,
+                     GsAppList *list,
+                     guint64 age,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       return gs_appstream_add_recent (plugin, priv->store, list, age,
+                                       cancellable, error);
+}
+
 static gboolean
 gs_plugin_appstream_refresh_url (GsPlugin *plugin,
                                 const gchar *url,
diff --git a/plugins/dummy/gs-plugin-dummy.c b/plugins/dummy/gs-plugin-dummy.c
index d2190c3..f1e3e52 100644
--- a/plugins/dummy/gs-plugin-dummy.c
+++ b/plugins/dummy/gs-plugin-dummy.c
@@ -676,6 +676,26 @@ gs_plugin_add_category_apps (GsPlugin *plugin,
 }
 
 gboolean
+gs_plugin_add_recent (GsPlugin *plugin,
+                     GsAppList *list,
+                     guint64 age,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       g_autoptr(GsApp) app = gs_app_new ("chiron.desktop");
+       gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron");
+       gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "View and use virtual machines");
+       gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, "http://www.box.org";);
+       gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
+       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+       gs_app_set_pixbuf (app, gdk_pixbuf_new_from_file 
("/usr/share/icons/hicolor/48x48/apps/chiron.desktop.png", NULL));
+       gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
+       gs_app_set_management_plugin (app, gs_plugin_get_name (plugin));
+       gs_app_list_add (list, app);
+       return TRUE;
+}
+
+gboolean
 gs_plugin_add_distro_upgrades (GsPlugin *plugin,
                               GsAppList *list,
                               GCancellable *cancellable,


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