/** * gs_app_row_get_description: * * Return value: PangoMarkup **/ static GString * gs_app_row_get_description (GsAppRow *app_row) { GsAppRowPrivate *priv = gs_app_row_get_instance_private (app_row); const gchar *tmp = NULL; /* convert the markdown update description into PangoMarkup */ if (priv->show_update && (gs_app_get_state (priv->app) == AS_APP_STATE_UPDATABLE || gs_app_get_state (priv->app) == AS_APP_STATE_UPDATABLE_LIVE)) { tmp = gs_app_get_update_details (priv->app); if (tmp != NULL && tmp[0] != '\0') return g_string_new (tmp); } /* if missing summary is set, return it without escaping in order to * correctly show hyperlinks */ if (gs_app_get_state (priv->app) == AS_APP_STATE_UNAVAILABLE) { tmp = gs_app_get_summary_missing (priv->app); if (tmp != NULL && tmp[0] != '\0') return g_string_new (tmp); } /* try all these things in order */ if (tmp == NULL || (tmp != NULL && tmp[0] == '\0')) tmp = gs_app_get_description (priv->app); if (tmp == NULL || (tmp != NULL && tmp[0] == '\0')) tmp = gs_app_get_summary (priv->app); if (tmp == NULL || (tmp != NULL && tmp[0] == '\0')) tmp = gs_app_get_name (priv->app); if (tmp == NULL) return NULL; return g_string_new (tmp); }
/** * gs_plugin_refine_item_state: */ static gboolean gs_plugin_refine_item_state (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); guint i; g_autoptr(GPtrArray) xrefs = NULL; g_autoptr(AsProfileTask) ptask = NULL; /* already found */ if (gs_app_get_state (app) != AS_APP_STATE_UNKNOWN) return TRUE; /* need broken out metadata */ if (!gs_plugin_refine_item_metadata (plugin, app, cancellable, error)) return FALSE; /* get apps and runtimes */ ptask = as_profile_start_literal (gs_plugin_get_profile (plugin), "xdg-app::refine-action"); xrefs = xdg_app_installation_list_installed_refs (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); /* check xref is app */ if (!gs_plugin_xdg_app_is_xref (app, XDG_APP_REF(xref))) continue; /* mark as installed */ g_debug ("marking %s as installed with xdg-app", gs_app_get_id (app)); gs_plugin_xdg_app_set_metadata_installed (app, xref); if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) gs_app_set_state (app, AS_APP_STATE_INSTALLED); } /* anything not installed just check the remote is still present */ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN && gs_app_get_origin (app) != NULL) { g_autoptr(XdgAppRemote) xremote = NULL; xremote = xdg_app_installation_get_remote_by_name (priv->installation, gs_app_get_origin (app), cancellable, NULL); if (xremote != NULL) { g_debug ("marking %s as available with xdg-app", gs_app_get_id (app)); gs_app_set_state (app, AS_APP_STATE_AVAILABLE); } } /* success */ return TRUE; }
static gboolean app_state_changed_idle (gpointer user_data) { GsFeatureTile *tile = GS_FEATURE_TILE (user_data); AtkObject *accessible; g_autofree gchar *name = NULL; accessible = gtk_widget_get_accessible (GTK_WIDGET (tile)); switch (gs_app_get_state (tile->app)) { case AS_APP_STATE_INSTALLED: case AS_APP_STATE_INSTALLING: case AS_APP_STATE_REMOVING: case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_UPDATABLE_LIVE: name = g_strdup_printf ("%s (%s)", gs_app_get_name (tile->app), _("Installed")); break; case AS_APP_STATE_AVAILABLE: default: name = g_strdup (gs_app_get_name (tile->app)); break; } if (GTK_IS_ACCESSIBLE (accessible)) { atk_object_set_name (accessible, name); atk_object_set_description (accessible, gs_app_get_summary (tile->app)); } g_object_unref (tile); return G_SOURCE_REMOVE; }
void gs_page_install_app (GsPage *page, GsApp *app, GsShellInteraction interaction, GCancellable *cancellable) { GsPagePrivate *priv = gs_page_get_instance_private (page); GsPageHelper *helper; GtkResponseType response; /* probably non-free */ if (gs_app_get_state (app) == AS_APP_STATE_UNAVAILABLE) { response = gs_app_notify_unavailable (app, gs_shell_get_window (priv->shell)); if (response != GTK_RESPONSE_OK) return; } helper = g_slice_new0 (GsPageHelper); helper->action = GS_PLUGIN_ACTION_INSTALL; helper->app = g_object_ref (app); helper->page = g_object_ref (page); helper->cancellable = g_object_ref (cancellable); helper->interaction = interaction; gs_plugin_loader_app_action_async (priv->plugin_loader, app, helper->action, GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY, helper->cancellable, gs_page_app_installed_cb, helper); }
gboolean gs_plugin_app_install (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { if (gs_app_get_state (app) != AS_APP_STATE_INSTALLED) return TRUE; return gs_plugin_app_set_usage_app (plugin, app, TRUE, error); }
gboolean gs_plugin_app_remove (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { if (gs_app_get_state (app) != AS_APP_STATE_AVAILABLE) return TRUE; return gs_plugin_app_set_usage_app (plugin, app, FALSE, error); }
static GsUpdateListSection gs_update_list_get_app_section (GsApp *app) { if (gs_app_get_state (app) == AS_APP_STATE_UPDATABLE_LIVE) { if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE) return GS_UPDATE_LIST_SECTION_ONLINE_FIRMWARE; return GS_UPDATE_LIST_SECTION_ONLINE; } if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE) return GS_UPDATE_LIST_SECTION_OFFLINE_FIRMWARE; return GS_UPDATE_LIST_SECTION_OFFLINE; }
/** * gs_plugin_add_updates: */ gboolean gs_plugin_add_updates (GsPlugin *plugin, GList **list, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); guint i; g_autoptr(GPtrArray) xrefs = NULL; /* get all the installed apps (no network I/O) */ xrefs = xdg_app_installation_list_installed_refs (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); const gchar *commit; const gchar *latest_commit; g_autoptr(GsApp) app = NULL; g_autoptr(GError) error_local = NULL; /* check the application has already been downloaded */ commit = xdg_app_ref_get_commit (XDG_APP_REF (xref)); latest_commit = xdg_app_installed_ref_get_latest_commit (xref); if (g_strcmp0 (commit, latest_commit) == 0) { g_debug ("no downloaded update for %s", xdg_app_ref_get_name (XDG_APP_REF (xref))); continue; } /* we have an update to show */ g_debug ("%s has a downloaded update %s->%s", xdg_app_ref_get_name (XDG_APP_REF (xref)), commit, latest_commit); app = gs_plugin_xdg_app_create_installed (plugin, xref, &error_local); if (app == NULL) { g_warning ("failed to add xdg-app: %s", error_local->message); continue; } if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) gs_app_set_state (app, AS_APP_STATE_UNKNOWN); gs_app_set_state (app, AS_APP_STATE_UPDATABLE_LIVE); gs_app_list_add (list, app); } return TRUE; }
static gboolean app_state_changed_idle (gpointer user_data) { GsPopularTile *tile = GS_POPULAR_TILE (user_data); AtkObject *accessible; GtkWidget *label; gboolean installed; g_autofree gchar *name = NULL; accessible = gtk_widget_get_accessible (GTK_WIDGET (tile)); label = gtk_bin_get_child (GTK_BIN (tile->eventbox)); switch (gs_app_get_state (tile->app)) { case AS_APP_STATE_INSTALLED: case AS_APP_STATE_INSTALLING: case AS_APP_STATE_REMOVING: case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_UPDATABLE_LIVE: installed = TRUE; name = g_strdup_printf ("%s (%s)", gs_app_get_name (tile->app), _("Installed")); /* TRANSLATORS: this is the small blue label on the tile * that tells the user the application is installed */ gtk_label_set_label (GTK_LABEL (label), _("Installed")); break; case AS_APP_STATE_AVAILABLE: default: installed = FALSE; name = g_strdup (gs_app_get_name (tile->app)); break; } gtk_widget_set_visible (tile->eventbox, installed); if (GTK_IS_ACCESSIBLE (accessible)) { atk_object_set_name (accessible, name); atk_object_set_description (accessible, gs_app_get_summary (tile->app)); } g_object_unref (tile); return G_SOURCE_REMOVE; }
gboolean gs_plugin_update_app (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { /* if we can process this online do not require a trigger */ if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE) return TRUE; /* only process this app if was created by this plugin */ if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) return TRUE; /* trigger offline update */ if (!pk_offline_trigger (PK_OFFLINE_ACTION_REBOOT, cancellable, error)) { gs_plugin_packagekit_error_convert (error); return FALSE; } /* success! */ return TRUE; }
static GsApp * gs_plugin_fwupd_new_app (GsPlugin *plugin, FwupdDevice *dev, GError **error) { FwupdRelease *rel = fwupd_device_get_release_default (dev); GPtrArray *checksums; const gchar *update_uri; g_autofree gchar *basename = NULL; g_autofree gchar *filename_cache = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GsApp) app = NULL; /* update unsupported */ app = gs_plugin_fwupd_new_app_from_device (plugin, dev); if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE_LIVE) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "%s [%s] cannot be updated", gs_app_get_name (app), gs_app_get_id (app)); return NULL; } /* some missing */ if (gs_app_get_id (app) == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "fwupd: No id for firmware"); return NULL; } if (gs_app_get_version (app) == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "fwupd: No version! for %s!", gs_app_get_id (app)); return NULL; } if (gs_app_get_update_version (app) == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "fwupd: No update-version! for %s!", gs_app_get_id (app)); return NULL; } checksums = fwupd_release_get_checksums (rel); if (checksums->len == 0) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NO_SECURITY, "%s [%s] (%s) has no checksums, ignoring as unsafe", gs_app_get_name (app), gs_app_get_id (app), gs_app_get_update_version (app)); return NULL; } update_uri = fwupd_release_get_uri (rel); if (update_uri == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_INVALID_FORMAT, "no location available for %s [%s]", gs_app_get_name (app), gs_app_get_id (app)); return NULL; } /* does the firmware already exist in the cache? */ basename = g_path_get_basename (update_uri); filename_cache = gs_utils_get_cache_filename ("fwupd", basename, GS_UTILS_CACHE_FLAG_NONE, error); if (filename_cache == NULL) return NULL; /* delete the file if the checksum does not match */ if (g_file_test (filename_cache, G_FILE_TEST_EXISTS)) { const gchar *checksum_tmp = NULL; g_autofree gchar *checksum = NULL; /* we can migrate to something better than SHA1 when the LVFS * starts producing metadata with multiple hash types */ checksum_tmp = fwupd_checksum_get_by_kind (checksums, G_CHECKSUM_SHA1); if (checksum_tmp == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_INVALID_FORMAT, "No valid checksum for %s", filename_cache); } checksum = gs_plugin_fwupd_get_file_checksum (filename_cache, G_CHECKSUM_SHA1, error); if (checksum == NULL) return NULL; if (g_strcmp0 (checksum_tmp, checksum) != 0) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_INVALID_FORMAT, "%s does not match checksum, expected %s got %s", filename_cache, checksum_tmp, checksum); g_unlink (filename_cache); return NULL; } } /* already downloaded, so overwrite */ if (g_file_test (filename_cache, G_FILE_TEST_EXISTS)) gs_app_set_size_download (app, 0); /* actually add the application */ file = g_file_new_for_path (filename_cache); gs_app_set_local_file (app, file); return g_steal_pointer (&app); }
static void gs_page_app_purchased_cb (GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data; GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source); GsPage *page = helper->page; GsPagePrivate *priv = gs_page_get_instance_private (page); gboolean ret; g_autoptr(GsPluginJob) plugin_job = NULL; g_autoptr(GError) error = NULL; ret = gs_plugin_loader_job_action_finish (plugin_loader, res, &error); if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED)) { g_debug ("%s", error->message); return; } if (!ret) { /* try to authenticate then retry */ if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_AUTH_REQUIRED)) { gs_page_authenticate (page, helper->app, gs_utils_get_error_value (error), helper->cancellable, gs_page_purchase_authenticate_cb, helper); g_steal_pointer (&helper); return; } else if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_PURCHASE_NOT_SETUP)) { const gchar *url; /* have we been given a link */ url = gs_utils_get_error_value (error); if (url != NULL) { g_autoptr(GError) error_local = NULL; g_debug ("showing link in: %s", error->message); if (!gtk_show_uri_on_window (GTK_WINDOW (gs_shell_get_window (priv->shell)), url, GDK_CURRENT_TIME, &error_local)) { g_warning ("failed to show URI %s: %s", url, error_local->message); } return; } } g_warning ("failed to purchase %s: %s", gs_app_get_id (helper->app), error->message); return; } if (gs_app_get_state (helper->app) != AS_APP_STATE_AVAILABLE) { g_warning ("no plugin purchased %s", gs_app_get_id (helper->app)); return; } /* now install */ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_INSTALL, "interactive", TRUE, "app", helper->app, NULL); gs_plugin_loader_job_process_async (priv->plugin_loader, plugin_job, helper->cancellable, gs_page_app_installed_cb, helper); g_steal_pointer (&helper); }
/** * gs_appstream_refine_app: */ gboolean gs_appstream_refine_app (GsPlugin *plugin, GsApp *app, AsApp *item, GError **error) { AsRelease *rel; GHashTable *urls; GPtrArray *pkgnames; GPtrArray *kudos; const gchar *tmp; guint i; /* set the kind to be more precise */ if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN || gs_app_get_kind (app) == AS_APP_KIND_GENERIC) { gs_app_set_kind (app, as_app_get_kind (item)); } /* is installed already */ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) { switch (as_app_get_source_kind (item)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_DESKTOP: gs_app_set_kind (app, AS_APP_KIND_DESKTOP); gs_app_set_state (app, AS_APP_STATE_INSTALLED); break; case AS_APP_SOURCE_KIND_METAINFO: gs_app_set_state (app, AS_APP_STATE_INSTALLED); break; case AS_APP_SOURCE_KIND_APPSTREAM: gs_app_set_state (app, as_app_get_state (item)); break; default: break; } } /* set management plugin automatically */ gs_refine_item_management_plugin (app, item); /* set id */ if (as_app_get_id (item) != NULL && gs_app_get_id (app) == NULL) gs_app_set_id (app, as_app_get_id (item)); /* set name */ tmp = as_app_get_name (item, NULL); if (tmp != NULL) { if (g_str_has_prefix (tmp, "(Nightly) ")) { tmp += 10; if (gs_app_get_metadata_item (app, "X-XdgApp-Tags") == NULL) gs_app_set_metadata (app, "X-XdgApp-Tags", "nightly"); } gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, tmp); } /* set summary */ tmp = as_app_get_comment (item, NULL); if (tmp != NULL) { gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST, tmp); } /* add urls */ urls = as_app_get_urls (item); if (g_hash_table_size (urls) > 0 && gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) { GList *l; g_autoptr(GList) keys = NULL; keys = g_hash_table_get_keys (urls); for (l = keys; l != NULL; l = l->next) { gs_app_set_url (app, as_url_kind_from_string (l->data), g_hash_table_lookup (urls, l->data)); } } /* set licence */ if (as_app_get_project_license (item) != NULL && gs_app_get_license (app) == NULL) gs_app_set_license (app, GS_APP_QUALITY_HIGHEST, as_app_get_project_license (item)); /* set keywords */ if (as_app_get_keywords (item, NULL) != NULL && gs_app_get_keywords (app) == NULL) { gs_app_set_keywords (app, as_app_get_keywords (item, NULL)); gs_app_add_kudo (app, GS_APP_KUDO_HAS_KEYWORDS); } /* set origin */ if (as_app_get_origin (item) != NULL && gs_app_get_origin (app) == NULL) { gs_app_set_origin (app, as_app_get_origin (item)); } /* set description */ tmp = as_app_get_description (item, NULL); if (tmp != NULL) { g_autofree gchar *from_xml = NULL; from_xml = as_markup_convert_simple (tmp, error); if (from_xml == NULL) { g_prefix_error (error, "trying to parse '%s': ", tmp); return FALSE; } gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, from_xml); } /* set icon */ if (as_app_get_icon_default (item) != NULL && gs_app_get_pixbuf (app) == NULL) gs_refine_item_pixbuf (plugin, app, item); /* set categories */ if (as_app_get_categories (item) != NULL && gs_app_get_categories (app)->len == 0) gs_app_set_categories (app, as_app_get_categories (item)); /* set project group */ if (as_app_get_project_group (item) != NULL && gs_app_get_project_group (app) == NULL) gs_app_set_project_group (app, as_app_get_project_group (item)); /* this is a core application for the desktop and cannot be removed */ if (_as_app_has_compulsory_for_desktop (item, "GNOME") && gs_app_get_kind (app) == AS_APP_KIND_DESKTOP) gs_app_add_quirk (app, AS_APP_QUIRK_COMPULSORY); /* set id kind */ if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN) gs_app_set_kind (app, as_app_get_kind (item)); /* copy all the metadata */ gs_appstream_copy_metadata (app, item); /* set package names */ pkgnames = as_app_get_pkgnames (item); if (pkgnames->len > 0 && gs_app_get_sources(app)->len == 0) gs_app_set_sources (app, pkgnames); /* set addons */ gs_appstream_refine_add_addons (plugin, app, item); /* set screenshots */ gs_appstream_refine_add_screenshots (app, item); /* are the screenshots perfect */ if (gs_appstream_are_screenshots_perfect (item)) gs_app_add_kudo (app, GS_APP_KUDO_PERFECT_SCREENSHOTS); /* was this application released recently */ if (gs_appstream_is_recent_release (item)) gs_app_add_kudo (app, GS_APP_KUDO_RECENT_RELEASE); /* add kudos */ if (as_app_get_language (item, plugin->locale) > 50) gs_app_add_kudo (app, GS_APP_KUDO_MY_LANGUAGE); /* add new-style kudos */ kudos = as_app_get_kudos (item); for (i = 0; i < kudos->len; i++) { tmp = g_ptr_array_index (kudos, i); switch (as_kudo_kind_from_string (tmp)) { case AS_KUDO_KIND_SEARCH_PROVIDER: gs_app_add_kudo (app, GS_APP_KUDO_SEARCH_PROVIDER); break; case AS_KUDO_KIND_USER_DOCS: gs_app_add_kudo (app, GS_APP_KUDO_INSTALLS_USER_DOCS); break; case AS_KUDO_KIND_APP_MENU: gs_app_add_kudo (app, GS_APP_KUDO_USES_APP_MENU); break; case AS_KUDO_KIND_MODERN_TOOLKIT: gs_app_add_kudo (app, GS_APP_KUDO_MODERN_TOOLKIT); break; case AS_KUDO_KIND_NOTIFICATIONS: gs_app_add_kudo (app, GS_APP_KUDO_USES_NOTIFICATIONS); break; case AS_KUDO_KIND_HIGH_CONTRAST: gs_app_add_kudo (app, GS_APP_KUDO_HIGH_CONTRAST); break; case AS_KUDO_KIND_HI_DPI_ICON: gs_app_add_kudo (app, GS_APP_KUDO_HI_DPI_ICON); break; default: g_debug ("no idea how to handle kudo '%s'", tmp); break; } } /* is there any update information */ rel = as_app_get_release_default (item); if (rel != NULL) { tmp = as_release_get_description (rel, NULL); if (tmp != NULL) { g_autofree gchar *desc = NULL; desc = as_markup_convert (tmp, AS_MARKUP_CONVERT_FORMAT_MARKDOWN, error); if (desc == NULL) return FALSE; gs_app_set_update_details (app, desc); } gs_app_set_update_urgency (app, as_release_get_urgency (rel)); gs_app_set_update_version (app, as_release_get_version (rel)); } return TRUE; }
void gs_page_remove_app (GsPage *page, GsApp *app, GCancellable *cancellable) { GsPagePrivate *priv = gs_page_get_instance_private (page); GsPageHelper *helper; GtkWidget *dialog; g_autofree gchar *message = NULL; g_autofree gchar *title = NULL; /* pending install */ helper = g_slice_new0 (GsPageHelper); helper->action = GS_PLUGIN_ACTION_REMOVE; helper->app = g_object_ref (app); helper->page = g_object_ref (page); helper->cancellable = g_object_ref (cancellable); if (gs_app_get_state (app) == AS_APP_STATE_QUEUED_FOR_INSTALL) { g_debug ("remove %s", gs_app_get_id (app)); gs_plugin_loader_app_action_async (priv->plugin_loader, app, GS_PLUGIN_ACTION_REMOVE, GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY, helper->cancellable, gs_page_app_removed_cb, helper); return; } /* use different name and summary */ switch (gs_app_get_kind (app)) { case AS_APP_KIND_SOURCE: /* TRANSLATORS: this is a prompt message, and '%s' is an * source name, e.g. 'GNOME Nightly' */ title = g_strdup_printf (_("Are you sure you want to remove " "the %s source?"), gs_app_get_name (app)); /* TRANSLATORS: longer dialog text */ message = g_strdup_printf (_("All applications from %s will be " "removed, and you will have to " "re-install the source to use them again."), gs_app_get_name (app)); break; default: /* TRANSLATORS: this is a prompt message, and '%s' is an * application summary, e.g. 'GNOME Clocks' */ title = g_strdup_printf (_("Are you sure you want to remove %s?"), gs_app_get_name (app)); /* TRANSLATORS: longer dialog text */ message = g_strdup_printf (_("%s will be removed, and you will " "have to install it to use it again."), gs_app_get_name (app)); break; } /* ask for confirmation */ dialog = gtk_message_dialog_new (gs_shell_get_window (priv->shell), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_CANCEL, "%s", title); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", message); /* TRANSLATORS: this is button text to remove the application */ gtk_dialog_add_button (GTK_DIALOG (dialog), _("Remove"), GTK_RESPONSE_OK); /* handle this async */ g_signal_connect (dialog, "response", G_CALLBACK (gs_page_remove_app_response_cb), helper); gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (dialog)); }
gboolean gs_plugin_app_install (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); GPtrArray *addons; GPtrArray *source_ids; ProgressData data; const gchar *package_id; guint i, j; g_autofree gchar *local_filename = NULL; g_auto(GStrv) package_ids = NULL; g_autoptr(GPtrArray) array_package_ids = NULL; g_autoptr(PkResults) results = NULL; data.app = app; data.plugin = plugin; data.ptask = NULL; /* only process this app if was created by this plugin */ if (g_strcmp0 (gs_app_get_management_plugin (app), gs_plugin_get_name (plugin)) != 0) return TRUE; /* we enable the repo */ if (gs_app_get_state (app) == AS_APP_STATE_UNAVAILABLE) { /* get everything up front we need */ source_ids = gs_app_get_source_ids (app); if (source_ids->len == 0) { g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "installing not available"); return FALSE; } package_ids = g_new0 (gchar *, 2); package_ids[0] = g_strdup (g_ptr_array_index (source_ids, 0)); /* enable the source */ if (!gs_plugin_app_source_enable (plugin, app, cancellable, error)) return FALSE; /* FIXME: this is a hack, to allow PK time to re-initialize * everything in order to match an actual result. The root cause * is probably some kind of hard-to-debug race in the daemon. */ g_usleep (G_USEC_PER_SEC * 3); /* actually install the package */ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); gs_app_set_state (app, AS_APP_STATE_INSTALLING); results = pk_task_install_packages_sync (priv->task, package_ids, cancellable, gs_plugin_packagekit_progress_cb, &data, error); if (!gs_plugin_packagekit_results_valid (results, error)) { gs_app_set_state_recover (app); return FALSE; } /* state is known */ gs_app_set_state (app, AS_APP_STATE_INSTALLED); /* no longer valid */ gs_app_clear_source_ids (app); return TRUE; }
void gs_page_install_app (GsPage *page, GsApp *app, GsShellInteraction interaction, GCancellable *cancellable) { GsPagePrivate *priv = gs_page_get_instance_private (page); GsPageHelper *helper; /* probably non-free */ if (gs_app_get_state (app) == AS_APP_STATE_UNAVAILABLE) { GtkResponseType response; response = gs_app_notify_unavailable (app, gs_shell_get_window (priv->shell)); if (response != GTK_RESPONSE_OK) return; } helper = g_slice_new0 (GsPageHelper); helper->action = GS_PLUGIN_ACTION_INSTALL; helper->app = g_object_ref (app); helper->page = g_object_ref (page); helper->cancellable = g_object_ref (cancellable); helper->interaction = interaction; /* need to purchase first */ if (gs_app_get_state (app) == AS_APP_STATE_PURCHASABLE) { GtkWidget *dialog; g_autofree gchar *title = NULL; g_autofree gchar *message = NULL; g_autofree gchar *price_text = NULL; /* TRANSLATORS: this is a prompt message, and '%s' is an * application summary, e.g. 'GNOME Clocks' */ title = g_strdup_printf (_("Are you sure you want to purchase %s?"), gs_app_get_name (app)); price_text = gs_price_to_string (gs_app_get_price (app)); /* TRANSLATORS: longer dialog text */ message = g_strdup_printf (_("%s will be installed, and you will " "be charged %s."), gs_app_get_name (app), price_text); dialog = gtk_message_dialog_new (gs_shell_get_window (priv->shell), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_CANCEL, "%s", title); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", message); /* TRANSLATORS: this is button text to purchase the application */ gtk_dialog_add_button (GTK_DIALOG (dialog), _("Purchase"), GTK_RESPONSE_OK); /* handle this async */ g_signal_connect (dialog, "response", G_CALLBACK (gs_page_install_purchase_response_cb), helper); gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (dialog)); } else { g_autoptr(GsPluginJob) plugin_job = NULL; plugin_job = gs_plugin_job_newv (helper->action, "interactive", TRUE, "app", helper->app, NULL); gs_plugin_loader_job_process_async (priv->plugin_loader, plugin_job, helper->cancellable, gs_page_app_installed_cb, helper); } }
static void refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_search, GCancellable *cancellable) { g_autofree gchar *macaroon = NULL; g_auto(GStrv) discharges = NULL; const gchar *status, *icon_url, *launch_name = NULL; g_autoptr(GdkPixbuf) icon_pixbuf = NULL; gint64 size = -1; get_macaroon (plugin, &macaroon, &discharges); status = json_object_get_string_member (package, "status"); if (g_strcmp0 (status, "installed") == 0 || g_strcmp0 (status, "active") == 0) { const gchar *update_available; update_available = json_object_has_member (package, "update_available") ? json_object_get_string_member (package, "update_available") : NULL; if (update_available) gs_app_set_state (app, AS_APP_STATE_UPDATABLE); else { if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE) gs_app_set_state (app, AS_APP_STATE_UNKNOWN); gs_app_set_state (app, AS_APP_STATE_INSTALLED); } } else if (g_strcmp0 (status, "not installed") == 0 || g_strcmp0 (status, "available") == 0) { gs_app_set_state (app, AS_APP_STATE_AVAILABLE); } gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, json_object_get_string_member (package, "summary")); gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST, json_object_get_string_member (package, "summary")); gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, json_object_get_string_member (package, "description")); gs_app_set_version (app, json_object_get_string_member (package, "version")); if (json_object_has_member (package, "installed-size")) { size = json_object_get_int_member (package, "installed-size"); if (size > 0) gs_app_set_size_installed (app, (guint64) size); } if (json_object_has_member (package, "download-size")) { size = json_object_get_int_member (package, "download-size"); if (size > 0) gs_app_set_size_download (app, (guint64) size); } gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE); icon_url = json_object_get_string_member (package, "icon"); if (g_str_has_prefix (icon_url, "/")) { g_autofree gchar *icon_data = NULL; gsize icon_data_length; g_autoptr(GError) error = NULL; icon_data = gs_snapd_get_resource (macaroon, discharges, icon_url, &icon_data_length, cancellable, &error); if (icon_data != NULL) { g_autoptr(GdkPixbufLoader) loader = NULL; loader = gdk_pixbuf_loader_new (); gdk_pixbuf_loader_write (loader, (guchar *) icon_data, icon_data_length, NULL); gdk_pixbuf_loader_close (loader, NULL); icon_pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)); } else g_printerr ("Failed to get icon: %s\n", error->message); } else { g_autoptr(SoupMessage) message = NULL; g_autoptr(GdkPixbufLoader) loader = NULL; message = soup_message_new (SOUP_METHOD_GET, icon_url); if (message != NULL) { soup_session_send_message (gs_plugin_get_soup_session (plugin), message); loader = gdk_pixbuf_loader_new (); gdk_pixbuf_loader_write (loader, (guint8 *) message->response_body->data, (gsize) message->response_body->length, NULL); gdk_pixbuf_loader_close (loader, NULL); icon_pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)); } } if (icon_pixbuf) { gs_app_set_pixbuf (app, icon_pixbuf); } else { g_autoptr(AsIcon) icon = as_icon_new (); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); as_icon_set_name (icon, "package-x-generic"); gs_app_add_icon (app, icon); } if (json_object_has_member (package, "screenshots") && gs_app_get_screenshots (app)->len <= 0) { JsonArray *screenshots; guint i; screenshots = json_object_get_array_member (package, "screenshots"); for (i = 0; i < json_array_get_length (screenshots); i++) { JsonObject *screenshot = json_array_get_object_element (screenshots, i); g_autoptr(AsScreenshot) ss = NULL; g_autoptr(AsImage) image = NULL; ss = as_screenshot_new (); as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL); image = as_image_new (); as_image_set_url (image, json_object_get_string_member (screenshot, "url")); as_image_set_kind (image, AS_IMAGE_KIND_SOURCE); as_screenshot_add_image (ss, image); gs_app_add_screenshot (app, ss); } } if (!from_search) { JsonArray *apps; apps = json_object_get_array_member (package, "apps"); if (apps && json_array_get_length (apps) > 0) launch_name = json_object_get_string_member (json_array_get_object_element (apps, 0), "name"); if (launch_name) gs_app_set_metadata (app, "snap::launch-name", launch_name); else gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE); } }
void gs_app_row_refresh (GsAppRow *app_row) { GsAppRowPrivate *priv = gs_app_row_get_instance_private (app_row); GtkStyleContext *context; GString *str = NULL; const gchar *tmp; gboolean missing_search_result; guint64 installed_size; if (priv->app == NULL) return; /* is this a missing search result from the extras page? */ missing_search_result = (gs_app_get_state (priv->app) == AS_APP_STATE_UNAVAILABLE && gs_app_get_url (priv->app, AS_URL_KIND_MISSING) != NULL); /* do a fill bar for the current progress */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_INSTALLING: gs_progress_button_set_progress (GS_PROGRESS_BUTTON (priv->button), gs_app_get_progress (priv->app)); gs_progress_button_set_show_progress (GS_PROGRESS_BUTTON (priv->button), TRUE); break; default: gs_progress_button_set_show_progress (GS_PROGRESS_BUTTON (priv->button), FALSE); break; } /* join the description lines */ str = gs_app_row_get_description (app_row); if (str != NULL) { as_utils_string_replace (str, "\n", " "); gtk_label_set_label (GTK_LABEL (priv->description_label), str->str); g_string_free (str, TRUE); } else { gtk_label_set_text (GTK_LABEL (priv->description_label), NULL); } /* add warning */ if (gs_app_has_quirk (priv->app, AS_APP_QUIRK_REMOVABLE_HARDWARE)) { gtk_label_set_text (GTK_LABEL (priv->label_warning), /* TRANSLATORS: during the update the device * will restart into a special update-only mode */ _("Device cannot be used during update.")); gtk_widget_show (priv->label_warning); } /* where did this app come from */ if (priv->show_source) { tmp = gs_app_get_origin_hostname (priv->app); if (tmp != NULL) { g_autofree gchar *origin_tmp = NULL; /* TRANSLATORS: this refers to where the app came from */ origin_tmp = g_strdup_printf ("%s: %s", _("Source"), tmp); gtk_label_set_label (GTK_LABEL (priv->label_origin), origin_tmp); } gtk_widget_set_visible (priv->label_origin, tmp != NULL); } else { gtk_widget_set_visible (priv->label_origin, FALSE); } /* installed tag */ if (!priv->show_buttons) { switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_UPDATABLE_LIVE: case AS_APP_STATE_INSTALLED: gtk_widget_set_visible (priv->label_installed, TRUE); break; default: gtk_widget_set_visible (priv->label_installed, FALSE); break; } } else { gtk_widget_set_visible (priv->label_installed, FALSE); } /* name */ if (g_strcmp0 (gs_app_get_branch (priv->app), "master") == 0) { g_autofree gchar *name = NULL; /* TRANSLATORS: not translated to match what flatpak does */ name = g_strdup_printf ("%s (Nightly)", gs_app_get_name (priv->app)); gtk_label_set_label (GTK_LABEL (priv->name_label), name); } else { gtk_label_set_label (GTK_LABEL (priv->name_label), gs_app_get_name (priv->app)); } if (priv->show_update && (gs_app_get_state (priv->app) == AS_APP_STATE_UPDATABLE || gs_app_get_state (priv->app) == AS_APP_STATE_UPDATABLE_LIVE)) { g_autofree gchar *verstr = NULL; verstr = gs_app_row_format_version_update (priv->app); gtk_label_set_label (GTK_LABEL (priv->version_label), verstr); gtk_widget_set_visible (priv->version_label, verstr != NULL); gtk_widget_hide (priv->star); } else { gtk_widget_hide (priv->version_label); if (missing_search_result || gs_app_get_rating (priv->app) <= 0) { gtk_widget_hide (priv->star); } else { gtk_widget_show (priv->star); gtk_widget_set_sensitive (priv->star, FALSE); gs_star_widget_set_rating (GS_STAR_WIDGET (priv->star), gs_app_get_rating (priv->app)); } gtk_label_set_label (GTK_LABEL (priv->version_label), gs_app_get_version_ui (priv->app)); } /* folders */ if (priv->show_folders && gs_utils_is_current_desktop ("GNOME") && g_settings_get_boolean (priv->settings, "show-folder-management")) { g_autoptr(GsFolders) folders = NULL; const gchar *folder; folders = gs_folders_get (); folder = gs_folders_get_app_folder (folders, gs_app_get_id (priv->app), gs_app_get_categories (priv->app)); if (folder != NULL) folder = gs_folders_get_folder_name (folders, folder); gtk_label_set_label (GTK_LABEL (priv->folder_label), folder); gtk_widget_set_visible (priv->folder_label, folder != NULL); } else { gtk_widget_hide (priv->folder_label); } /* pixbuf */ if (gs_app_get_pixbuf (priv->app) != NULL) gs_image_set_from_pixbuf (GTK_IMAGE (priv->image), gs_app_get_pixbuf (priv->app)); context = gtk_widget_get_style_context (priv->image); if (missing_search_result) gtk_style_context_add_class (context, "dimmer-label"); else gtk_style_context_remove_class (context, "dimmer-label"); /* pending label */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_QUEUED_FOR_INSTALL: gtk_widget_set_visible (priv->label, TRUE); gtk_label_set_label (GTK_LABEL (priv->label), _("Pending")); break; default: gtk_widget_set_visible (priv->label, FALSE); break; } /* spinner */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_REMOVING: gtk_spinner_start (GTK_SPINNER (priv->spinner)); gtk_widget_set_visible (priv->spinner, TRUE); break; default: gtk_widget_set_visible (priv->spinner, FALSE); break; } /* button */ gs_app_row_refresh_button (app_row, missing_search_result); /* hide buttons in the update list, unless the app is live updatable */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_UPDATABLE_LIVE: case AS_APP_STATE_INSTALLING: gtk_widget_set_visible (priv->button_box, TRUE); break; default: gtk_widget_set_visible (priv->button_box, !priv->show_update); break; } /* checkbox */ if (priv->selectable) { if (gs_app_get_kind (priv->app) == AS_APP_KIND_DESKTOP || gs_app_get_kind (priv->app) == AS_APP_KIND_RUNTIME || gs_app_get_kind (priv->app) == AS_APP_KIND_WEB_APP) gtk_widget_set_visible (priv->checkbox, TRUE); } else { gtk_widget_set_visible (priv->checkbox, FALSE); } installed_size = gs_app_get_size_installed (priv->app); if (priv->show_installed_size && installed_size != GS_APP_SIZE_UNKNOWABLE && installed_size != 0) { g_autofree gchar *size = NULL; size = g_format_size (installed_size); gtk_label_set_label (GTK_LABEL (priv->label_app_size), size); gtk_widget_show (priv->label_app_size); } else { gtk_widget_hide (priv->label_app_size); } }
static void gs_app_row_refresh_button (GsAppRow *app_row, gboolean missing_search_result) { GsAppRowPrivate *priv = gs_app_row_get_instance_private (app_row); GtkStyleContext *context; /* disabled */ if (!priv->show_buttons) { gtk_widget_set_visible (priv->button, FALSE); return; } /* label */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_UNAVAILABLE: gtk_widget_set_visible (priv->button, TRUE); if (missing_search_result) { /* TRANSLATORS: this is a button next to the search results that * allows the application to be easily installed */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Visit website")); } else { /* TRANSLATORS: this is a button next to the search results that * allows the application to be easily installed. * The ellipsis indicates that further steps are required */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Install…")); } break; case AS_APP_STATE_QUEUED_FOR_INSTALL: gtk_widget_set_visible (priv->button, TRUE); /* TRANSLATORS: this is a button next to the search results that * allows to cancel a queued install of the application */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Cancel")); /* TRANSLATORS: this is a label that describes an application * that has been queued for installation */ break; case AS_APP_STATE_AVAILABLE: case AS_APP_STATE_AVAILABLE_LOCAL: gtk_widget_set_visible (priv->button, TRUE); /* TRANSLATORS: this is a button next to the search results that * allows the application to be easily installed */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Install")); break; case AS_APP_STATE_UPDATABLE_LIVE: gtk_widget_set_visible (priv->button, TRUE); if (priv->show_update) { /* TRANSLATORS: this is a button in the updates panel * that allows the app to be easily updated live */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Update")); } else { /* TRANSLATORS: this is a button next to the search results that * allows the application to be easily removed */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Remove")); } break; case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_INSTALLED: if (!gs_app_has_quirk (priv->app, AS_APP_QUIRK_COMPULSORY)) gtk_widget_set_visible (priv->button, TRUE); /* TRANSLATORS: this is a button next to the search results that * allows the application to be easily removed */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Remove")); break; case AS_APP_STATE_INSTALLING: gtk_widget_set_visible (priv->button, TRUE); /* TRANSLATORS: this is a button next to the search results that * shows the status of an application being installed */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Installing")); break; case AS_APP_STATE_REMOVING: gtk_widget_set_visible (priv->button, TRUE); /* TRANSLATORS: this is a button next to the search results that * shows the status of an application being erased */ gtk_button_set_label (GTK_BUTTON (priv->button), _("Removing")); break; default: break; } /* visible */ switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_UNAVAILABLE: case AS_APP_STATE_QUEUED_FOR_INSTALL: case AS_APP_STATE_AVAILABLE: case AS_APP_STATE_AVAILABLE_LOCAL: case AS_APP_STATE_UPDATABLE_LIVE: case AS_APP_STATE_INSTALLING: case AS_APP_STATE_REMOVING: gtk_widget_set_visible (priv->button, TRUE); break; case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_INSTALLED: gtk_widget_set_visible (priv->button, !gs_app_has_quirk (priv->app, AS_APP_QUIRK_COMPULSORY)); break; default: gtk_widget_set_visible (priv->button, FALSE); break; } /* colorful */ context = gtk_widget_get_style_context (priv->button); if (!priv->colorful) { gtk_style_context_remove_class (context, "destructive-action"); } else { switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_UPDATABLE: case AS_APP_STATE_INSTALLED: case AS_APP_STATE_UPDATABLE_LIVE: gtk_style_context_remove_class (context, "destructive-action"); break; default: gtk_style_context_add_class (context, "destructive-action"); break; } } /* always insensitive when in selection mode */ if (priv->selectable) { gtk_widget_set_sensitive (priv->button, FALSE); } else { switch (gs_app_get_state (priv->app)) { case AS_APP_STATE_INSTALLING: case AS_APP_STATE_REMOVING: gtk_widget_set_sensitive (priv->button, FALSE); break; default: gtk_widget_set_sensitive (priv->button, TRUE); break; } } }
/** * gs_plugin_refine_item_size: */ static gboolean gs_plugin_refine_item_size (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); gboolean ret; guint64 download_size; guint64 installed_size; g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GError) error_local = NULL; /* already set */ if (gs_app_get_size_installed (app) > 0 && gs_app_get_size_download (app) > 0) return TRUE; /* need commit */ if (!gs_plugin_refine_item_commit (plugin, app, cancellable, error)) return FALSE; /* need runtime */ if (!gs_plugin_refine_item_runtime (plugin, app, cancellable, error)) return FALSE; /* calculate the platform size too if the app is not installed */ if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE && gs_app_get_xdgapp_kind (app) == XDG_APP_REF_KIND_APP) { GsApp *app_runtime; /* find out what runtime the application depends on */ if (!gs_plugin_refine_item_runtime (plugin, app, cancellable, error)) return FALSE; /* is the app_runtime already installed? */ app_runtime = gs_app_get_runtime (app); if (!gs_plugin_refine_item_state (plugin, app_runtime, cancellable, error)) return FALSE; if (gs_app_get_state (app_runtime) == AS_APP_STATE_INSTALLED) { g_debug ("runtime %s is already installed, so not adding size", gs_app_get_id (app_runtime)); } else { if (!gs_plugin_refine_item_size (plugin, app_runtime, cancellable, error)) return FALSE; } } /* just get the size of the runtime */ ptask = as_profile_start_literal (gs_plugin_get_profile (plugin), "xdg-app::refine-size"); ret = xdg_app_installation_fetch_remote_size_sync (priv->installation, gs_app_get_origin (app), gs_app_get_xdgapp_commit (app), &download_size, &installed_size, cancellable, &error_local); if (!ret) { g_warning ("libxdgapp failed to return application size: %s", error_local->message); gs_app_set_size_installed (app, GS_APP_SIZE_UNKNOWABLE); gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE); } else { gs_app_set_size_installed (app, installed_size); gs_app_set_size_download (app, download_size); } return TRUE; }
/** * gs_plugin_app_install: */ gboolean gs_plugin_app_install (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); g_autoptr(XdgAppInstalledRef) xref = NULL; /* only process this app if was created by this plugin */ if (g_strcmp0 (gs_app_get_management_plugin (app), gs_plugin_get_name (plugin)) != 0) return TRUE; /* ensure we have metadata and state */ if (!gs_plugin_xdg_app_refine_app (plugin, app, 0, cancellable, error)) return FALSE; /* install */ gs_app_set_state (app, AS_APP_STATE_INSTALLING); /* install required runtime if not already installed */ if (gs_app_get_kind (app) == AS_APP_KIND_DESKTOP) { GsApp *runtime; runtime = gs_app_get_runtime (app); /* the runtime could come from a different remote to the app */ if (!gs_plugin_refine_item_metadata (plugin, runtime, cancellable, error)) return FALSE; if (!gs_plugin_refine_item_origin (plugin, runtime, cancellable, error)) return FALSE; if (!gs_plugin_refine_item_state (plugin, runtime, cancellable, error)) return FALSE; if (gs_app_get_state (runtime) == AS_APP_STATE_UNKNOWN) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "Failed to find runtime %s", gs_app_get_source_default (runtime)); return FALSE; } /* not installed */ if (gs_app_get_state (runtime) == AS_APP_STATE_AVAILABLE) { g_debug ("%s is not already installed, so installing", gs_app_get_id (runtime)); gs_app_set_state (runtime, AS_APP_STATE_INSTALLING); xref = xdg_app_installation_install (priv->installation, gs_app_get_origin (runtime), gs_app_get_xdgapp_kind (runtime), gs_app_get_xdgapp_name (runtime), gs_app_get_xdgapp_arch (runtime), gs_app_get_xdgapp_branch (runtime), gs_plugin_xdg_app_progress_cb, app, cancellable, error); if (xref == NULL) { gs_app_set_state_recover (runtime); return FALSE; } gs_app_set_state (runtime, AS_APP_STATE_INSTALLED); } else { g_debug ("%s is already installed, so skipping", gs_app_get_id (runtime)); } } /* use the source for local apps */ if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE_LOCAL) { xref = xdg_app_installation_install_bundle (priv->installation, gs_app_get_local_file (app), gs_plugin_xdg_app_progress_cb, app, cancellable, error); } else { g_debug ("installing %s", gs_app_get_id (app)); xref = xdg_app_installation_install (priv->installation, gs_app_get_origin (app), gs_app_get_xdgapp_kind (app), gs_app_get_xdgapp_name (app), gs_app_get_xdgapp_arch (app), gs_app_get_xdgapp_branch (app), gs_plugin_xdg_app_progress_cb, app, cancellable, error); } if (xref == NULL) { gs_app_set_state_recover (app); return FALSE; } /* state is known */ gs_app_set_state (app, AS_APP_STATE_INSTALLED); return TRUE; }