[gnome-software/1476-add-a-way-for-app-developers-to-test-their-metainfo-appdata-files] application: Add '--show-appdata' command line argument



commit d8d5b0b7e526fe337f0b7a115bb8a6213c641dfe
Author: Milan Crha <mcrha redhat com>
Date:   Thu Oct 14 12:27:05 2021 +0200

    application: Add '--show-appdata' command line argument
    
    This can be used to view an appstream data file how it'll be shown
    in the Software, without a need to install it and search for it.
    
    Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1476

 src/gs-application.c  |  30 ++++++++++++
 src/gs-details-page.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/gs-details-page.h |   2 +
 src/gs-shell.c        |  17 ++++++-
 src/gs-shell.h        |   2 +
 5 files changed, 172 insertions(+), 5 deletions(-)
---
diff --git a/src/gs-application.c b/src/gs-application.c
index b62931bc1..0969428c8 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -143,6 +143,8 @@ gs_application_init (GsApplication *application)
                { "interaction", '\0', 0, G_OPTION_ARG_STRING, NULL,
                  _("The kind of interaction expected for this action: either "
                    "‘none’, ‘notify’, or ‘full’"), NULL },
+               { "show-appdata", '\0', 0, G_OPTION_ARG_FILENAME, NULL,
+                 _("Show a local appdata file"), _("FILENAME") },
                { "verbose", '\0', 0, G_OPTION_ARG_NONE, NULL,
                  _("Show verbose debugging information"), NULL },
                { "autoupdate", 0, 0, G_OPTION_ARG_NONE, NULL,
@@ -786,6 +788,23 @@ filename_activated (GSimpleAction *action,
        gs_shell_show_local_file (app->shell, file);
 }
 
+static void
+show_appdata_activated (GSimpleAction *action,
+                       GVariant      *parameter,
+                       gpointer       data)
+{
+       GsApplication *app = GS_APPLICATION (data);
+       const gchar *filename;
+       g_autoptr(GFile) file = NULL;
+
+       g_variant_get (parameter, "(&s)", &filename);
+
+       file = g_file_new_for_path (filename);
+
+       gs_shell_reset_state (app->shell);
+       gs_shell_show_appdata (app->shell, file);
+}
+
 static void
 launch_activated (GSimpleAction *action,
                  GVariant      *parameter,
@@ -903,6 +922,7 @@ static GActionEntry actions_after_loading[] = {
        { "install", install_activated, "(su)", NULL, NULL },
        { "filename", filename_activated, "(s)", NULL, NULL },
        { "install-resources", install_resources_activated, "(sassss)", NULL, NULL },
+       { "show-appdata", show_appdata_activated, "(s)", NULL, NULL },
        { "nop", NULL, NULL, NULL }
 };
 
@@ -1221,6 +1241,16 @@ gs_application_handle_local_options (GApplication *app, GVariantDict *options)
                                                "filename",
                                                g_variant_new ("(s)", absolute_filename));
                rc = 0;
+       } else if (g_variant_dict_lookup (options, "show-appdata", "^&ay", &local_filename)) {
+               g_autoptr(GFile) file = NULL;
+               g_autofree gchar *absolute_filename = NULL;
+
+               file = g_file_new_for_path (local_filename);
+               absolute_filename = g_file_get_path (file);
+               g_action_group_activate_action (G_ACTION_GROUP (app),
+                                               "show-appdata",
+                                               g_variant_new ("(s)", absolute_filename));
+               rc = 0;
        }
 
        return rc;
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index e0a7ad2b5..ccbef2898 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -14,6 +14,8 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
+#include "lib/gs-appstream.h"
+
 #include "gs-common.h"
 #include "gs-utils.h"
 
@@ -1482,7 +1484,8 @@ _set_app (GsDetailsPage *self, GsApp *app)
 
 /* show the UI and do operations that should not block page load */
 static void
-gs_details_page_load_stage2 (GsDetailsPage *self)
+gs_details_page_load_stage2 (GsDetailsPage *self,
+                            gboolean continue_loading)
 {
        g_autofree gchar *tmp = NULL;
        g_autoptr(GsPluginJob) plugin_job1 = NULL;
@@ -1504,6 +1507,9 @@ gs_details_page_load_stage2 (GsDetailsPage *self)
        gs_details_page_refresh_all (self);
        gs_details_page_update_origin_button (self, FALSE);
 
+       if (!continue_loading)
+               return;
+
        /* if these tasks fail (e.g. because we have no networking) then it's
         * of no huge importance if we don't get the required data */
        plugin_job1 = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
@@ -1570,7 +1576,7 @@ gs_details_page_load_stage1_cb (GObject *source,
        }
 
        /* do 2nd stage refine */
-       gs_details_page_load_stage2 (self);
+       gs_details_page_load_stage2 (self, TRUE);
 }
 
 static void
@@ -1594,7 +1600,7 @@ gs_details_page_file_to_app_cb (GObject *source,
                GsApp *app = gs_app_list_index (list, 0);
                g_set_object (&self->app_local_file, app);
                _set_app (self, app);
-               gs_details_page_load_stage2 (self);
+               gs_details_page_load_stage2 (self, TRUE);
        }
 }
 
@@ -1618,7 +1624,7 @@ gs_details_page_url_to_app_cb (GObject *source,
        } else {
                GsApp *app = gs_app_list_index (list, 0);
                _set_app (self, app);
-               gs_details_page_load_stage2 (self);
+               gs_details_page_load_stage2 (self, TRUE);
        }
 }
 
@@ -2475,3 +2481,115 @@ gs_details_page_set_is_narrow (GsDetailsPage *self, gboolean is_narrow)
        self->is_narrow = is_narrow;
        g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_IS_NARROW]);
 }
+
+static void
+gs_details_page_appdata_ready_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       GsDetailsPage *self = GS_DETAILS_PAGE (source_object);
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GError) error = NULL;
+
+       app = g_task_propagate_pointer (G_TASK (result), &error);
+       if (error) {
+               gtk_label_set_text (GTK_LABEL (self->label_failed), error->message);
+               gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_FAILED);
+               return;
+       }
+
+       g_set_object (&self->app_local_file, app);
+       _set_app (self, app);
+       gs_details_page_load_stage2 (self, FALSE);
+}
+
+static void
+gs_details_page_appdata_thread (GTask *task,
+                               gpointer source_object,
+                               gpointer task_data,
+                               GCancellable *cancellable)
+{
+       const gchar *const *locales;
+       g_autofree gchar *xml = NULL;
+       g_autofree gchar *path = NULL;
+       g_autoptr(XbBuilder) builder = NULL;
+       g_autoptr(XbBuilderSource) builder_source = NULL;
+       g_autoptr(XbSilo) silo = NULL;
+       g_autoptr(GPtrArray) nodes = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsApp) app = NULL;
+       GFile *file = task_data;
+       XbNode *component;
+
+       builder_source = xb_builder_source_new ();
+       if (!xb_builder_source_load_file (builder_source, file, XB_BUILDER_SOURCE_FLAG_NONE, cancellable, 
&error)) {
+               g_task_return_error (task, g_steal_pointer (&error));
+               return;
+       }
+
+       builder = xb_builder_new ();
+       locales = g_get_language_names ();
+
+       /* add current locales */
+       for (guint i = 0; locales[i] != NULL; i++) {
+               xb_builder_add_locale (builder, locales[i]);
+       }
+
+       xb_builder_import_source (builder, builder_source);
+
+       silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID | 
XB_BUILDER_COMPILE_FLAG_SINGLE_LANG, cancellable, &error);
+       if (silo == NULL) {
+               g_task_return_error (task, g_steal_pointer (&error));
+               return;
+       }
+
+       nodes = xb_silo_query (silo, "component", 0, NULL);
+       if (nodes == NULL)
+               nodes = xb_silo_query (silo, "application", 0, NULL);
+       if (nodes == NULL) {
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                       "Passed-in file doesn't have a 'component' (nor 'application') top-level element");
+               return;
+       }
+
+       if (nodes->len != 1) {
+               g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Only one top-level element expected, received %u instead", nodes->len);
+               return;
+       }
+
+       component = g_ptr_array_index (nodes, 0);
+
+       app = gs_appstream_create_app (NULL, silo, component, &error);
+       if (app == NULL) {
+               g_task_return_error (task, g_steal_pointer (&error));
+               return;
+       }
+
+       if (!gs_appstream_refine_app (NULL, app, silo, component, GS_DETAILS_PAGE_REFINE_FLAGS, &error)) {
+               g_task_return_error (task, g_steal_pointer (&error));
+               return;
+       }
+
+       path = g_file_get_path (file);
+       gs_app_set_origin (app, path);
+
+       gs_app_set_state (app, GS_APP_STATE_UNKNOWN);
+
+       g_task_return_pointer (task, g_steal_pointer (&app), g_object_unref);
+}
+
+void
+gs_details_page_set_appdata (GsDetailsPage *self,
+                            GFile *file)
+{
+       g_autoptr(GTask) task = NULL;
+       gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_LOADING);
+       g_clear_object (&self->app_local_file);
+       g_clear_object (&self->app);
+       self->origin_by_packaging_format = FALSE;
+       task = g_task_new (self, self->cancellable, gs_details_page_appdata_ready_cb, NULL);
+       g_task_set_source_tag (task, gs_details_page_set_appdata);
+       g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+       g_task_run_in_thread (task, gs_details_page_appdata_thread);
+}
diff --git a/src/gs-details-page.h b/src/gs-details-page.h
index 1ec5059d1..bf1a697f1 100644
--- a/src/gs-details-page.h
+++ b/src/gs-details-page.h
@@ -33,5 +33,7 @@ void           gs_details_page_set_odrs_provider      (GsDetailsPage  *self,
 gboolean        gs_details_page_get_is_narrow  (GsDetailsPage  *self);
 void            gs_details_page_set_is_narrow  (GsDetailsPage  *self,
                                                 gboolean        is_narrow);
+void            gs_details_page_set_appdata    (GsDetailsPage *self,
+                                                GFile *file);
 
 G_END_DECLS
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 9e2cde8c8..710c34bfe 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -620,7 +620,10 @@ gs_shell_change_mode (GsShell *shell,
                gtk_editable_set_position (GTK_EDITABLE (shell->entry_search), -1);
        } else if (mode == GS_SHELL_MODE_DETAILS) {
                app = GS_APP (data);
-               if (gs_app_get_local_file (app) != NULL) {
+               if (gs_app_get_metadata_item (app, "GnomeSoftware::show-appdata") != NULL) {
+                       gs_details_page_set_appdata (GS_DETAILS_PAGE (page),
+                                                    gs_app_get_local_file (app));
+               } else if (gs_app_get_local_file (app) != NULL) {
                        gs_details_page_set_local_file (GS_DETAILS_PAGE (page),
                                                        gs_app_get_local_file (app));
                } else if (gs_app_get_metadata_item (app, "GnomeSoftware::from-url") != NULL) {
@@ -2359,6 +2362,18 @@ gs_shell_show_local_file (GsShell *shell, GFile *file)
        gs_shell_activate (shell);
 }
 
+void
+gs_shell_show_appdata (GsShell *shell, GFile *file)
+{
+       g_autoptr(GsApp) app = gs_app_new (NULL);
+       save_back_entry (shell);
+       gs_app_set_metadata (app, "GnomeSoftware::show-appdata", "1");
+       gs_app_set_local_file (app, file);
+       gs_shell_change_mode (shell, GS_SHELL_MODE_DETAILS,
+                             (gpointer) app, TRUE);
+       gs_shell_activate (shell);
+}
+
 void
 gs_shell_show_search_result (GsShell *shell, const gchar *id, const gchar *search)
 {
diff --git a/src/gs-shell.h b/src/gs-shell.h
index d11345b46..73e4c072c 100644
--- a/src/gs-shell.h
+++ b/src/gs-shell.h
@@ -86,5 +86,7 @@ void           gs_shell_setup                 (GsShell        *shell,
 void            gs_shell_show_notification     (GsShell        *shell,
                                                 const gchar    *title);
 gboolean        gs_shell_get_is_narrow         (GsShell        *shell);
+void            gs_shell_show_appdata          (GsShell        *shell,
+                                                GFile          *file);
 
 G_END_DECLS


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