[gnome-initial-setup/wip/rancell/ubuntu-welcome] Add Ubuntu mode with special pages
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-initial-setup/wip/rancell/ubuntu-welcome] Add Ubuntu mode with special pages
- Date: Wed, 18 Apr 2018 08:20:45 +0000 (UTC)
commit e430685fe5abf7e058e5f27045f30e84493d7791
Author: Robert Ancell <robert ancell canonical com>
Date: Tue Mar 20 11:51:30 2018 +1300
Add Ubuntu mode with special pages
.gitignore | 5 +
configure.ac | 9 +-
data/Makefile.am | 6 +
data/com.ubuntu.welcome.policy.in | 21 +
gnome-initial-setup/Makefile.am | 4 +
gnome-initial-setup/gnome-initial-setup.c | 40 ++-
gnome-initial-setup/pages/Makefile.am | 6 +-
gnome-initial-setup/pages/apps/Makefile.am | 21 +
gnome-initial-setup/pages/apps/apps.gresource.xml | 8 +
.../pages/apps/default-snap-icon.svg | 1 +
gnome-initial-setup/pages/apps/gis-apps-page.c | 592 ++++++++++++++++++++
gnome-initial-setup/pages/apps/gis-apps-page.h | 52 ++
gnome-initial-setup/pages/apps/gis-apps-page.ui | 95 ++++
gnome-initial-setup/pages/livepatch/Makefile.am | 21 +
.../pages/livepatch/gis-livepatch-page.c | 486 ++++++++++++++++
.../pages/livepatch/gis-livepatch-page.h | 52 ++
.../pages/livepatch/gis-livepatch-page.ui | 143 +++++
.../pages/livepatch/livepatch.gresource.xml | 8 +
gnome-initial-setup/pages/livepatch/livepatch.svg | 1 +
.../pages/privacy/gis-privacy-page.c | 2 +-
.../pages/ubuntu-changes/Makefile.am | 21 +
.../pages/ubuntu-changes/gis-ubuntu-changes-page.c | 106 ++++
.../pages/ubuntu-changes/gis-ubuntu-changes-page.h | 52 ++
.../ubuntu-changes/gis-ubuntu-changes-page.ui | 62 ++
.../ubuntu-changes/ubuntu-changes.gresource.xml | 8 +
.../pages/ubuntu-changes/ubuntu-changes.png | Bin 0 -> 182199 bytes
.../pages/ubuntu-report/Makefile.am | 21 +
.../pages/ubuntu-report/gis-ubuntu-report-page.c | 243 ++++++++
.../pages/ubuntu-report/gis-ubuntu-report-page.h | 52 ++
.../pages/ubuntu-report/gis-ubuntu-report-page.ui | 184 ++++++
.../ubuntu-report/ubuntu-report.gresource.xml | 8 +
.../pages/ubuntu-report/ubuntu-report.svg | 1 +
po/POTFILES.in | 9 +
33 files changed, 2335 insertions(+), 5 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 3844b01..ea59dd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,7 +65,12 @@ gnome-initial-setup/pages/account/account-resources.[ch]
gnome-initial-setup/pages/account/um-realm-generated.[ch]
gnome-initial-setup/pages/software/software-resources.[ch]
gnome-initial-setup/pages/summary/summary-resources.[ch]
+gnome-initial-setup/pages/ubuntu-report/ubuntu-report-resources.[ch]
+gnome-initial-setup/pages/livepatch/livepatch-resources.[ch]
+gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes-resources.[ch]
+gnome-initial-setup/pages/apps/apps-resources.[ch]
+com.ubuntu.welcome.policy
gnome-initial-setup-first-login.desktop
gnome-initial-setup.desktop
gnome-initial-setup-copy-worker.desktop
diff --git a/configure.ac b/configure.ac
index ce17c55..45288c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,7 +61,10 @@ PKG_CHECK_MODULES(INITIAL_SETUP,
json-glib-1.0
libsecret-1
pwquality
- webkit2gtk-4.0)
+ webkit2gtk-4.0
+ snapd-glib
+ libsoup-2.4
+ sysmetrics)
INITIAL_SETUP_CFLAGS="$INITIAL_SETUP_CFLAGS -DNM_VERSION_MIN_REQUIRED=NM_VERSION_1_2"
INITIAL_SETUP_CFLAGS="$INITIAL_SETUP_CFLAGS -DNM_VERSION_MAX_ALLOWED=NM_VERSION_1_2"
@@ -157,6 +160,10 @@ gnome-initial-setup/pages/account/Makefile
gnome-initial-setup/pages/password/Makefile
gnome-initial-setup/pages/software/Makefile
gnome-initial-setup/pages/summary/Makefile
+gnome-initial-setup/pages/ubuntu-report/Makefile
+gnome-initial-setup/pages/livepatch/Makefile
+gnome-initial-setup/pages/ubuntu-changes/Makefile
+gnome-initial-setup/pages/apps/Makefile
po/Makefile.in
])
AC_OUTPUT
diff --git a/data/Makefile.am b/data/Makefile.am
index f9a9e12..ef6b767 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -11,6 +11,10 @@ edit = $(AM_V_GEN) sed \
$(edit) $< >$@
@INTLTOOL_DESKTOP_RULE@
+@INTLTOOL_POLICY_RULE@
+
+actionsdir = $(datadir)/polkit-1/actions
+actions_DATA = com.ubuntu.welcome.policy
rulesdir = $(datadir)/polkit-1/rules.d
rules_DATA = 20-gnome-initial-setup.rules
@@ -29,6 +33,7 @@ desktop_DATA = \
EXTRA_DIST = \
20-gnome-initial-setup.rules \
+ com.ubuntu.welcome.policy.in \
gnome-welcome-tour \
setup-shell.desktop \
gnome-welcome-tour.desktop.in.in \
@@ -40,6 +45,7 @@ EXTRA_DIST = \
$(NULL)
CLEANFILES = \
+ com.ubuntu.welcome.policy \
gnome-initial-setup.desktop.in \
gnome-initial-setup.desktop \
gnome-initial-setup-copy-worker.desktop.in \
diff --git a/data/com.ubuntu.welcome.policy.in b/data/com.ubuntu.welcome.policy.in
new file mode 100644
index 0000000..35424df
--- /dev/null
+++ b/data/com.ubuntu.welcome.policy.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+ <vendor>Ubuntu Welcome</vendor>
+ <vendor_url>https://www.ubuntu.com/</vendor_url>
+
+ <action id="com.ubuntu.welcome.livepatch">
+ <_description>Manage Livepatch</_description>
+ <_message>Authentication is required to enable Livepatch</_message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.imply">io.snapcraft.snapd.login
com.ubuntu.softwareproperties.applychanges </annotate>
+ </action>
+
+</policyconfig>
\ No newline at end of file
diff --git a/gnome-initial-setup/Makefile.am b/gnome-initial-setup/Makefile.am
index c04f1e4..6c36682 100644
--- a/gnome-initial-setup/Makefile.am
+++ b/gnome-initial-setup/Makefile.am
@@ -44,6 +44,10 @@ gnome_initial_setup_LDADD = \
pages/password/libgispassword.la \
pages/software/libgissoftware.la \
pages/summary/libgissummary.la \
+ pages/ubuntu-report/libgisubuntu-report.la \
+ pages/livepatch/libgislivepatch.la \
+ pages/ubuntu-changes/libgisubuntu-changes.la \
+ pages/apps/libgisapps.la \
$(INITIAL_SETUP_LIBS) \
-lm
diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
index 722c9ea..08dda87 100644
--- a/gnome-initial-setup/gnome-initial-setup.c
+++ b/gnome-initial-setup/gnome-initial-setup.c
@@ -48,6 +48,10 @@
#include "pages/account/gis-account-pages.h"
#include "pages/password/gis-password-page.h"
#include "pages/summary/gis-summary-page.h"
+#include "pages/ubuntu-report/gis-ubuntu-report-page.h"
+#include "pages/livepatch/gis-livepatch-page.h"
+#include "pages/ubuntu-changes/gis-ubuntu-changes-page.h"
+#include "pages/apps/gis-apps-page.h"
#define VENDOR_PAGES_GROUP "pages"
#define VENDOR_PAGES_SKIP_KEY "skip"
@@ -80,6 +84,16 @@ static PageData page_table[] = {
{ NULL },
};
+static PageData ubuntu_page_table[] = {
+ PAGE (ubuntu_changes, FALSE),
+ PAGE (livepatch, FALSE),
+ PAGE (ubuntu_report, FALSE),
+ PAGE (account, TRUE),
+ PAGE (password, TRUE),
+ PAGE (apps, FALSE),
+ { NULL },
+};
+
#undef PAGE
static gboolean
@@ -155,6 +169,25 @@ destroy_pages_after (GisAssistant *assistant,
}
}
+static gboolean
+is_desktop (const gchar *name)
+{
+ const gchar *xdg_current_desktop;
+ g_auto(GStrv) tokens = NULL;
+ int i;
+
+ xdg_current_desktop = g_getenv ("XDG_CURRENT_DESKTOP");
+ if (xdg_current_desktop == NULL)
+ return FALSE;
+
+ tokens = g_strsplit (xdg_current_desktop, ":", -1);
+ for (i = 0; tokens[i] != NULL; i++)
+ if (strcmp (tokens[i], name) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
static void
rebuild_pages_cb (GisDriver *driver)
{
@@ -169,12 +202,15 @@ rebuild_pages_cb (GisDriver *driver)
skip_pages = pages_to_skip_from_file ();
- page_data = page_table;
+ if (is_desktop ("ubuntu"))
+ page_data = ubuntu_page_table;
+ else
+ page_data = page_table;
if (current_page != NULL) {
destroy_pages_after (assistant, current_page);
- for (page_data = page_table; page_data->page_id != NULL; ++page_data)
+ for (page_data = ubuntu_page_table; page_data->page_id != NULL; ++page_data) // FIXME conditional
if (g_str_equal (page_data->page_id, GIS_PAGE_GET_CLASS (current_page)->page_id))
break;
diff --git a/gnome-initial-setup/pages/Makefile.am b/gnome-initial-setup/pages/Makefile.am
index e5182a7..551c5a4 100644
--- a/gnome-initial-setup/pages/Makefile.am
+++ b/gnome-initial-setup/pages/Makefile.am
@@ -11,4 +11,8 @@ SUBDIRS = \
account \
password \
summary \
- software
+ software \
+ ubuntu-report \
+ livepatch \
+ ubuntu-changes \
+ apps
diff --git a/gnome-initial-setup/pages/apps/Makefile.am b/gnome-initial-setup/pages/apps/Makefile.am
new file mode 100644
index 0000000..8badc4d
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libgisapps.la
+
+BUILT_SOURCES =
+
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies
$(srcdir)/apps.gresource.xml)
+apps-resources.c: apps.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
+apps-resources.h: apps.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
+BUILT_SOURCES += apps-resources.c apps-resources.h
+
+libgisapps_la_SOURCES = \
+ gis-apps-page.c gis-apps-page.h \
+ $(BUILT_SOURCES)
+
+libgisapps_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.." -I "$(top_srcdir)" -I "$(top_builddir)"
+libgisapps_la_LIBADD = $(INITIAL_SETUP_LIBS)
+libgisapps_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+EXTRA_DIST = apps.gresource.xml $(resource_files)
diff --git a/gnome-initial-setup/pages/apps/apps.gresource.xml
b/gnome-initial-setup/pages/apps/apps.gresource.xml
new file mode 100644
index 0000000..e89e744
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/apps.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/initial-setup">
+ <file preprocess="xml-stripblanks" alias="gis-apps-page.ui">gis-apps-page.ui</file>
+ <file preprocess="xml-stripblanks">default-snap-icon.svg</file>
+ </gresource>
+</gresources>
+
diff --git a/gnome-initial-setup/pages/apps/default-snap-icon.svg
b/gnome-initial-setup/pages/apps/default-snap-icon.svg
new file mode 100644
index 0000000..0d8dd94
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/default-snap-icon.svg
@@ -0,0 +1 @@
+<svg width="96" height="96" xmlns="http://www.w3.org/2000/svg"><circle fill="#F7F7F7" cx="48" cy="48"
r="48"/><path d="M55.71 53.294V32.766l14.248 6.28L55.71 53.293zM28.901 80.102L46.049 47.74l7.728 7.486-24.875
24.876zM19 19l35.502 12.8v22.702L19 19zm58.204 12.8L83 43.634 56.675 31.8h20.529z" fill="#CDCDCD"/></svg>
\ No newline at end of file
diff --git a/gnome-initial-setup/pages/apps/gis-apps-page.c b/gnome-initial-setup/pages/apps/gis-apps-page.c
new file mode 100644
index 0000000..f0b9406
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/gis-apps-page.c
@@ -0,0 +1,592 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Get more apps page {{{1 */
+
+#define PAGE_ID "apps"
+
+#include "config.h"
+#include "gis-apps-page.h"
+#include "apps-resources.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <snapd-glib/snapd-glib.h>
+#include <libsoup/soup.h>
+
+struct _GisAppsPagePrivate {
+ SoupSession *soup_session;
+ GtkWidget *featured_stack;
+ GtkWidget *prev_featured_button;
+ GtkWidget *next_featured_button;
+};
+typedef struct _GisAppsPagePrivate GisAppsPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisAppsPage, gis_apps_page, GIS_TYPE_PAGE);
+
+struct _GisSnapTile
+{
+ GtkFlowBoxChild parent;
+ SnapdSnap *snap;
+ SoupSession *soup_session;
+ GtkWidget *icon;
+};
+G_DECLARE_FINAL_TYPE (GisSnapTile, gis_snap_tile, GIS, SNAP_TILE, GtkFlowBoxChild)
+G_DEFINE_TYPE (GisSnapTile, gis_snap_tile, GTK_TYPE_FLOW_BOX_CHILD)
+
+static void
+gis_snap_tile_init (GisSnapTile *item)
+{
+}
+
+static void
+gis_snap_tile_finalize (GObject *object)
+{
+ g_clear_object (&GIS_SNAP_TILE (object)->snap);
+ g_clear_object (&GIS_SNAP_TILE (object)->soup_session);
+ G_OBJECT_CLASS (gis_snap_tile_parent_class)->finalize (object);
+}
+
+static void
+gis_snap_tile_class_init (GisSnapTileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gis_snap_tile_finalize;
+}
+
+static void
+gis_apps_page_constructed (GObject *object)
+{
+ GisAppsPage *page = GIS_APPS_PAGE (object);
+ GisAppsPagePrivate *priv = gis_apps_page_get_instance_private (page);
+
+ G_OBJECT_CLASS (gis_apps_page_parent_class)->constructed (object);
+
+ gis_page_set_skippable (GIS_PAGE (page), TRUE);
+
+ gis_page_set_complete (GIS_PAGE (page), TRUE);
+ gtk_widget_show (GTK_WIDGET (page));
+}
+
+static void
+gis_apps_page_dispose (GObject *object)
+{
+ GisAppsPage *page = GIS_APPS_PAGE (object);
+ GisAppsPagePrivate *priv = gis_apps_page_get_instance_private (page);
+
+ g_clear_object (&priv->soup_session);
+
+ G_OBJECT_CLASS (gis_apps_page_parent_class)->dispose (object);
+}
+
+static void
+open_software (GtkButton *button,
+ const gchar *uri,
+ GisAppsPage *page)
+{
+ g_autofree gchar *command = NULL;
+ g_autoptr(GAppInfo) info = NULL;
+ g_autoptr(GError) error = NULL;
+
+ info = g_app_info_create_from_commandline ("gnome-software", NULL, G_APP_INFO_CREATE_NONE, &error);
+ if (info == NULL) {
+ g_warning ("Failed to get launch information from gnome-software: %s", error->message);
+ return;
+ }
+ if (!g_app_info_launch (info, NULL, NULL, &error)) {
+ g_warning ("Failed to launch gnome-software: %s", error->message);
+ return;
+ }
+}
+
+static gboolean
+gis_apps_page_apply (GisPage *page, GCancellable *cancellable)
+{
+ gis_ensure_stamp_files ();
+ g_application_quit (G_APPLICATION (GIS_PAGE (page)->driver));
+ return FALSE;
+}
+
+static void
+gis_apps_page_locale_changed (GisPage *page)
+{
+ gis_page_set_title (GIS_PAGE (page), _("Get more apps"));
+}
+
+static void
+next_page (GtkWidget *stack, GtkWidget *prev_button, GtkWidget *next_button)
+{
+ GtkWidget *visible_child;
+ GList *children, *link;
+
+ visible_child = gtk_stack_get_visible_child (GTK_STACK (stack));
+ children = gtk_container_get_children (GTK_CONTAINER (stack));
+ for (link = children; link != NULL; link = link->next) {
+ GtkWidget *child = link->data;
+ if (child == visible_child) {
+ if (link->next == NULL)
+ return;
+
+ link = link->next;
+ gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+ gtk_stack_set_visible_child (GTK_STACK (stack), link->data);
+ gtk_widget_set_sensitive (prev_button, TRUE);
+ gtk_widget_set_sensitive (next_button, link->next != NULL);
+ return;
+ }
+ }
+}
+
+static void
+prev_page (GtkWidget *stack, GtkWidget *prev_button, GtkWidget *next_button)
+{
+ GtkWidget *visible_child;
+ GList *children, *link;
+
+ visible_child = gtk_stack_get_visible_child (GTK_STACK (stack));
+ children = gtk_container_get_children (GTK_CONTAINER (stack));
+ for (link = children; link != NULL; link = link->next) {
+ GtkWidget *child = link->data;
+ if (child == visible_child) {
+ if (link->prev == NULL)
+ return;
+
+ link = link->prev;
+ gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
+ gtk_stack_set_visible_child (GTK_STACK (stack), link->data);
+ gtk_widget_set_sensitive (prev_button, link->prev != NULL);
+ gtk_widget_set_sensitive (next_button, TRUE);
+ return;
+ }
+ }
+}
+
+static void
+prev_featured (GtkButton *button,
+ GisAppsPage *page)
+{
+ GisAppsPagePrivate *priv = gis_apps_page_get_instance_private (page);
+ prev_page (priv->featured_stack, priv->prev_featured_button, priv->next_featured_button);
+}
+
+static void
+next_featured (GtkButton *button,
+ GisAppsPage *page)
+{
+ GisAppsPagePrivate *priv = gis_apps_page_get_instance_private (page);
+ next_page (priv->featured_stack, priv->prev_featured_button, priv->next_featured_button);
+}
+
+static void
+gis_apps_page_class_init (GisAppsPageClass *klass)
+{
+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass),
"/org/gnome/initial-setup/gis-apps-page.ui");
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAppsPage, featured_stack);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAppsPage, prev_featured_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAppsPage, next_featured_button);
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), open_software);
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), prev_featured);
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), next_featured);
+
+ page_class->page_id = PAGE_ID;
+ page_class->apply = gis_apps_page_apply;
+ page_class->locale_changed = gis_apps_page_locale_changed;
+ object_class->constructed = gis_apps_page_constructed;
+ object_class->dispose = gis_apps_page_dispose;
+}
+
+static GdkPixbuf *
+load_snap_icon (SnapdSnap *snap)
+{
+ const gchar *url;
+ g_autoptr(SnapdClient) client = NULL;
+ g_autoptr(SnapdIcon) icon = NULL;
+ g_autoptr(GInputStream) input_stream = NULL;
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+ g_autoptr(GError) error = NULL;
+
+ url = snapd_snap_get_icon (snap);
+ if (url == NULL || !g_str_has_prefix (url, "/v2/icons/"))
+ return NULL;
+
+ client = snapd_client_new ();
+ icon = snapd_client_get_icon_sync (client, snapd_snap_get_name (snap), NULL, &error);
+ if (icon == NULL) {
+ g_warning ("Failed to get snap icon: %s", error->message);
+ return NULL;
+ }
+ input_stream = g_memory_input_stream_new_from_bytes (snapd_icon_get_data (icon));
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (input_stream, 48, 48, TRUE, NULL, &error);
+ if (pixbuf == NULL) {
+ g_warning ("Failed to decode snap icon: %s", error->message);
+ return NULL;
+ }
+
+ return g_steal_pointer (&pixbuf);
+}
+
+static GdkPixbuf *
+load_local_icon (const gchar *name, GError **error)
+{
+ if (g_str_has_prefix (name, "/"))
+ return gdk_pixbuf_new_from_file_at_scale (name, 48, 48, TRUE, error);
+ else {
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), name, 48, 0, error);
+ if (pixbuf == NULL)
+ return NULL;
+
+ if (gdk_pixbuf_get_width (pixbuf) == 48 && gdk_pixbuf_get_height (pixbuf) == 48)
+ return g_steal_pointer (&pixbuf);
+
+ return gdk_pixbuf_scale_simple (pixbuf, 48, 48, GDK_INTERP_BILINEAR);
+ }
+}
+
+static GdkPixbuf *
+load_desktop_icon (SnapdSnap *snap)
+{
+ GPtrArray *apps;
+ guint i;
+
+ apps = snapd_snap_get_apps (snap);
+ for (i = 0; i < apps->len; i++) {
+ SnapdApp *app = g_ptr_array_index (apps, i);
+ const gchar *desktop_file_path;
+ g_autoptr(GKeyFile) desktop_file = NULL;
+ g_autofree gchar *icon = NULL;
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+ g_autoptr(GError) error = NULL;
+
+ desktop_file_path = snapd_app_get_desktop_file (app);
+ if (desktop_file_path == NULL)
+ continue;
+
+ desktop_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (desktop_file, desktop_file_path, G_KEY_FILE_NONE, &error)) {
+ g_warning ("Failed to load desktop file %s: %s", desktop_file_path, error->message);
+ continue;
+ }
+
+ icon = g_key_file_get_string (desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON,
&error);
+ if (icon == NULL) {
+ g_warning ("Failed to get desktop file icon %s: %s", desktop_file_path, error->message);
+ continue;
+ }
+
+ pixbuf = load_local_icon (icon, &error);
+ if (pixbuf == NULL) {
+ g_warning ("Failed to load icon %s: %s", icon, error->message);
+ continue;
+ }
+
+ return g_steal_pointer (&pixbuf);
+ }
+
+ return NULL;
+}
+
+static void
+icon_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ GisSnapTile *tile = user_data;
+ g_autoptr(GInputStream) stream = NULL;
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+ g_autoptr(GError) error = NULL;
+
+ stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
+ if (stream == NULL) {
+ g_warning ("Failed to download icon: %s", error->message);
+ return;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, 48, 48, TRUE, NULL, &error);
+ if (pixbuf == NULL) {
+ g_warning ("Failed to load icon: %s", error->message);
+ return;
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (tile->icon), pixbuf);
+}
+
+static void
+load_store_icon (GisSnapTile *tile)
+{
+ const gchar *url;
+ g_autoptr(SoupMessage) message = NULL;
+
+ url = snapd_snap_get_icon (tile->snap);
+ if (url == NULL || !(g_str_has_prefix (url, "http://") || g_str_has_prefix (url, "https://")))
+ return;
+
+ message = soup_message_new ("GET", url);
+ soup_session_send_async (tile->soup_session, message, NULL, icon_cb, tile);
+}
+
+static void
+load_icon (GisSnapTile *tile)
+{
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+ g_autoptr(GError) error = NULL;
+
+ pixbuf = load_snap_icon (tile->snap);
+ if (pixbuf != NULL) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (tile->icon), pixbuf);
+ return;
+ }
+
+ pixbuf = load_desktop_icon (tile->snap);
+ if (pixbuf != NULL) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (tile->icon), pixbuf);
+ return;
+ }
+
+ /* Add placeholder icon */
+ pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/org/gnome/initial-setup/default-snap-icon.svg", 48, 48,
TRUE, &error);
+ if (pixbuf != NULL)
+ gtk_image_set_from_pixbuf (GTK_IMAGE (tile->icon), pixbuf);
+
+ load_store_icon (tile);
+}
+
+static GtkWidget *
+gis_snap_tile_new (SnapdSnap *snap, SoupSession *soup_session)
+{
+ const gchar *title;
+ GisSnapTile *tile;
+ GtkWidget *box, *icon, *label;
+
+ tile = g_object_new (gis_snap_tile_get_type (), NULL);
+ tile->snap = g_object_ref (snap);
+ tile->soup_session = g_object_ref (soup_session);
+
+ title = snapd_snap_get_title (snap);
+ if (title == NULL)
+ title = snapd_snap_get_name (snap);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_widget_show (box);
+ gtk_container_add (GTK_CONTAINER (tile), box);
+
+ tile->icon = gtk_image_new ();
+ gtk_widget_show (tile->icon);
+ gtk_box_pack_start (GTK_BOX (box), tile->icon, FALSE, TRUE, 0);
+
+ label = gtk_label_new (title);
+ gtk_widget_show (label);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+
+ load_icon (tile);
+
+ return GTK_WIDGET (tile);
+}
+
+static gboolean
+contains_snap (GPtrArray *snaps, const gchar *name)
+{
+ guint i;
+
+ for (i = 0; i < snaps->len; i++) {
+ SnapdSnap *snap = g_ptr_array_index (snaps, i);
+ if (g_strcmp0 (snapd_snap_get_name (snap), name) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GPtrArray *
+get_installed_apps (void)
+{
+ g_autoptr(GPtrArray) apps = NULL;
+ const gchar * const *data_dirs;
+ g_autoptr(GHashTable) checked_dirs = NULL;
+ int i;
+
+ apps = g_ptr_array_new_with_free_func ((GDestroyNotify) g_key_file_unref);
+
+ data_dirs = g_get_system_data_dirs ();
+ checked_dirs = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; data_dirs[i] != NULL; i++) {
+ g_autofree gchar *apps_dir = NULL;
+ g_autoptr(GDir) dir = NULL;
+ const gchar *filename;
+
+ if (g_hash_table_contains (checked_dirs, data_dirs[i]))
+ continue;
+ g_hash_table_add (checked_dirs, (gpointer) data_dirs[i]);
+
+ apps_dir = g_build_filename (data_dirs[i], "applications", NULL);
+ dir = g_dir_open (apps_dir, 0, NULL);
+ if (dir == NULL)
+ continue;
+
+ while ((filename = g_dir_read_name (dir)) != NULL) {
+ g_autofree gchar *path = NULL;
+ g_autoptr(GKeyFile) desktop_file = NULL;
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) categories = NULL;
+ g_auto(GStrv) not_show_in = NULL;
+
+ if (!g_str_has_suffix (filename, ".desktop"))
+ continue;
+
+ /* Blacklist some things that aren't really apps */
+ if (strcmp (filename, "display-im6.q16.desktop") == 0 ||
+ strcmp (filename, "ubuntu-amazon-default.desktop") == 0 ||
+ strcmp (filename, "org.gnome.Software.desktop") == 0)
+ continue;
+
+ path = g_build_filename (apps_dir, filename, NULL);
+ desktop_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (desktop_file, path, G_KEY_FILE_NONE, &error)) {
+ g_warning ("Failed to load desktop files %s: %s", path, error->message);
+ continue;
+ }
+
+ if (g_key_file_get_boolean (desktop_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL))
+ continue;
+
+ not_show_in = g_key_file_get_string_list (desktop_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
+ if (not_show_in != NULL && g_strv_contains ((const gchar *const *) not_show_in, "GNOME"))
+ continue;
+
+ /* Skip settings */
+ categories = g_key_file_get_string_list (desktop_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL, NULL);
+ if (categories != NULL && g_strv_contains ((const gchar *const *) categories, "Settings"))
+ continue;
+
+ /* Put in random order */
+ g_ptr_array_insert (apps, g_random_int_range (0, apps->len + 1), g_steal_pointer (&desktop_file));
+ }
+ }
+
+ return g_steal_pointer (&apps);
+}
+
+static void
+on_app_clicked (GtkWidget *flow_box, GtkFlowBoxChild *child, gpointer user_data)
+{
+ GisSnapTile *tile = GIS_SNAP_TILE (child);
+ g_autofree gchar *url = NULL;
+ g_autoptr (GError) error = NULL;
+
+ url = g_strdup_printf ("snap://%s", snapd_snap_get_name (tile->snap));
+ if (!g_app_info_launch_default_for_uri (url, NULL, &error))
+ g_warning ("Failed to open %s: %s", url, error->message);
+}
+
+static void
+add_tile (GtkWidget *stack, GtkWidget *tile)
+{
+ GList *children;
+ GtkWidget *flow_box = NULL;
+ gint count = 0;
+
+ children = gtk_container_get_children (GTK_CONTAINER (stack));
+ if (children != NULL) {
+ flow_box = g_list_last (children)->data;
+ count = g_list_length (gtk_container_get_children (GTK_CONTAINER (flow_box)));
+ }
+ if (flow_box == NULL || count >= 15) {
+ count = 0;
+ flow_box = gtk_flow_box_new ();
+ gtk_flow_box_set_min_children_per_line (GTK_FLOW_BOX (flow_box), 5);
+ gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (flow_box), GTK_SELECTION_NONE);
+ g_signal_connect (flow_box, "child-activated",
+ G_CALLBACK (on_app_clicked), NULL);
+ gtk_widget_show (flow_box);
+ gtk_flow_box_set_row_spacing (GTK_FLOW_BOX (flow_box), 10);
+ gtk_flow_box_set_column_spacing (GTK_FLOW_BOX (flow_box), 10);
+ gtk_container_add (GTK_CONTAINER (stack), flow_box);
+ }
+
+ gtk_container_add (GTK_CONTAINER (flow_box), tile);
+}
+
+static void
+gis_apps_page_init (GisAppsPage *page)
+{
+ GisAppsPagePrivate *priv = gis_apps_page_get_instance_private (page);
+ g_autoptr(SnapdClient) client = NULL;
+ GtkSizeGroup *size_group;
+ g_autoptr(GPtrArray) installed_apps = NULL;
+ g_autoptr(GPtrArray) installed_snaps = NULL;
+ g_autoptr(GPtrArray) featured_snaps = NULL;
+ guint i;
+ g_autoptr(GError) error = NULL;
+
+ g_resources_register (apps_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (page));
+
+ priv->soup_session = soup_session_new ();
+
+ client = snapd_client_new ();
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+
+ installed_apps = get_installed_apps ();
+ for (i = 0; i < installed_apps->len; i++) {
+ }
+
+ installed_snaps = snapd_client_list_sync (client, NULL, &error);
+ if (installed_snaps == NULL)
+ g_warning ("Failed to get installed snaps: %s", error->message);
+ featured_snaps = snapd_client_find_section_sync (client, SNAPD_FIND_FLAGS_NONE, "featured", NULL, NULL,
NULL, &error);
+ if (featured_snaps != NULL) {
+ guint i;
+
+ for (i = 0; i < featured_snaps->len; i++) {
+ SnapdSnap *snap = g_ptr_array_index (featured_snaps, i);
+ GtkWidget *tile, *icon;
+
+ /* Skip if already installed */
+ if (contains_snap (installed_snaps, snapd_snap_get_name (snap)))
+ continue;
+
+ /* Ignore common snaps that have default .debs */
+ if (g_strcmp0 (snapd_snap_get_name (snap), "firefox") == 0)
+ continue;
+
+ tile = gis_snap_tile_new (snap, priv->soup_session);
+ gtk_widget_show (tile);
+ gtk_size_group_add_widget (size_group, tile);
+
+ add_tile (priv->featured_stack, tile);
+ }
+ }
+ else
+ g_warning ("Failed to get featured snaps: %s", error->message);
+ gtk_widget_set_sensitive (priv->next_featured_button, g_list_length (gtk_container_get_children
(GTK_CONTAINER (priv->featured_stack))) > 1);
+}
+
+void
+gis_prepare_apps_page (GisDriver *driver)
+{
+ gis_driver_add_page (driver,
+ g_object_new (GIS_TYPE_APPS_PAGE,
+ "driver", driver,
+ NULL));
+}
diff --git a/gnome-initial-setup/pages/apps/gis-apps-page.h b/gnome-initial-setup/pages/apps/gis-apps-page.h
new file mode 100644
index 0000000..347dcbc
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/gis-apps-page.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIS_APPS_PAGE_H__
+#define __GIS_APPS_PAGE_H__
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_APPS_PAGE (gis_apps_page_get_type ())
+#define GIS_APPS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_APPS_PAGE, GisAppsPage))
+#define GIS_APPS_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_APPS_PAGE,
GisAppsPageClass))
+#define GIS_IS_APPS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_APPS_PAGE))
+#define GIS_IS_APPS_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_APPS_PAGE))
+#define GIS_APPS_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_APPS_PAGE,
GisAppsPageClass))
+
+typedef struct _GisAppsPage GisAppsPage;
+typedef struct _GisAppsPageClass GisAppsPageClass;
+
+struct _GisAppsPage
+{
+ GisPage parent;
+};
+
+struct _GisAppsPageClass
+{
+ GisPageClass parent_class;
+};
+
+GType gis_apps_page_get_type (void);
+
+void gis_prepare_apps_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_APPS_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/apps/gis-apps-page.ui b/gnome-initial-setup/pages/apps/gis-apps-page.ui
new file mode 100644
index 0000000..4a25f58
--- /dev/null
+++ b/gnome-initial-setup/pages/apps/gis-apps-page.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <template class="GisAppsPage" parent="GisPage">
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">fill</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">20</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes">Get more apps</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.8"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_top">36</property>
+ <property name="label" translatable="yes">You can use “Software” to install apps like
these:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">horizontal</property>
+ <property name="margin_top">36</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkButton" id="prev_featured_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="relief">none</property>
+ <signal name="clicked" handler="prev_featured"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="featured_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">18</property>
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="next_featured_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="relief">none</property>
+ <signal name="clicked" handler="next_featured"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">go-next-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="margin_top">18</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Open “Software” now</property>
+ <signal name="clicked" handler="open_software"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnome-initial-setup/pages/livepatch/Makefile.am b/gnome-initial-setup/pages/livepatch/Makefile.am
new file mode 100644
index 0000000..e40c6dc
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libgislivepatch.la
+
+BUILT_SOURCES =
+
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies
$(srcdir)/livepatch.gresource.xml)
+livepatch-resources.c: livepatch.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
+livepatch-resources.h: livepatch.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
+BUILT_SOURCES += livepatch-resources.c livepatch-resources.h
+
+libgislivepatch_la_SOURCES = \
+ gis-livepatch-page.c gis-livepatch-page.h \
+ $(BUILT_SOURCES)
+
+libgislivepatch_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.." -I "$(top_srcdir)" -I
"$(top_builddir)"
+libgislivepatch_la_LIBADD = $(INITIAL_SETUP_LIBS)
+libgislivepatch_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+EXTRA_DIST = livepatch.gresource.xml $(resource_files)
diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
new file mode 100644
index 0000000..d73d288
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
@@ -0,0 +1,486 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Andrea Azzarone <andrea azzarone canonical com>
+ */
+
+/* Canonical Livepatch page {{{1 */
+
+#define PAGE_ID "livepatch"
+
+#include "config.h"
+#include "gis-livepatch-page.h"
+#include "livepatch-resources.h"
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
+#include <goabackend/goabackend.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <libsecret/secret.h>
+#include <polkit/polkit.h>
+
+struct _GisLivepatchPagePrivate {
+ GtkWidget *setup_button;
+ GtkWidget *message_box;
+ GtkWidget *signout_button;
+ GtkWidget *message_label;
+
+ GoaClient *goa_client;
+ GoaAccount *goa_account;
+ GPermission *permission;
+
+ gchar *token;
+ gboolean waiting_for_livepatch_response;
+ gboolean user_current_choice;
+};
+typedef struct _GisLivepatchPagePrivate GisLivepatchPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisLivepatchPage, gis_livepatch_page, GIS_TYPE_PAGE);
+
+#define LIVEPATCH_ENABLE_SUCCESS_MESSAGE _("You're all set: Livepatch is now on.")
+#define LIVEPATCH_ENABLE_FAILURE_MESSAGE _("Failed to setup Livepatch: please retry.")
+#define LIVEPATCH_DISABLE_FAILURE_MESSAGE _("Failed to disable Livepatch: please retry.")
+
+static gboolean
+set_livepatch_enabled (GisLivepatchPage *page,
+ gboolean value);
+
+static gboolean
+is_livepatch_enabled ()
+{
+ return g_file_test ("/var/snap/canonical-livepatch/common/machine-token",
+ G_FILE_TEST_EXISTS);
+}
+
+static void
+on_livepatch_enabled (GObject *source_object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ g_autoptr(GVariant) result = NULL;
+ gboolean success = TRUE;
+ gboolean current_state = is_livepatch_enabled ();
+ g_autoptr(GError) error = NULL;
+
+ priv->waiting_for_livepatch_response = FALSE;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
+ if (result == NULL) {
+ g_warning ("Failed to enable/disable Livepatch through DBus: %s\n", error->message);
+ success = FALSE;
+ } else {
+ gboolean out_error;
+ g_autofree gchar *out_message = NULL;
+
+ g_variant_get (result, "(bs)", &out_error, &out_message);
+ if (out_error) {
+ g_warning ("Failed to enable/disable Livepatch: %s\n", out_message);
+ success = FALSE;
+ }
+ }
+
+ if (success) {
+ /* We failed to enable or disable livepatch.
+ Check if this corresponds to the current user choice. */
+ if (current_state != priv->user_current_choice) {
+ set_livepatch_enabled (page, priv->user_current_choice);
+ } else if (current_state) {
+ gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_SUCCESS_MESSAGE);
+ }
+ } else if (current_state != priv->user_current_choice) {
+ /* We failed to enable or disable livepatch. Show an error message if
+ the current state does not correpond the current user choice.
+ Ignore the message if the call failed but the current status correponds
+ to the user choice. */
+ if (priv->user_current_choice) {
+ gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_FAILURE_MESSAGE);
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ } else {
+ gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_DISABLE_FAILURE_MESSAGE);
+ }
+ }
+}
+
+static GoaAccount*
+add_ubuntusso_account (GisLivepatchPage *page)
+{
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ GtkWindow *parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page)));
+ GtkWidget *dialog;
+ g_autoptr(GoaProvider) provider = NULL;
+ g_autoptr(GError) error = NULL;
+ GoaObject *goa_object = NULL;
+ GoaAccount *ret = NULL;
+
+ dialog = gtk_dialog_new_with_buttons (_("Add Account"),
+ parent,
+ GTK_DIALOG_MODAL
+ | GTK_DIALOG_DESTROY_WITH_PARENT
+ | GTK_DIALOG_USE_HEADER_BAR,
+ NULL, NULL);
+
+ /* Set dialog to not resize. */
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ provider = goa_provider_get_for_provider_type ("ubuntusso");
+ if (provider == NULL) {
+ g_warning ("Failed to get an ubuntusso provider: %s", error->message);
+ goto out;
+ }
+
+ /* This actually show the login dialog */
+ goa_object = goa_provider_add_account (provider,
+ priv->goa_client,
+ GTK_DIALOG (dialog),
+ GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ &error);
+
+ if (error) {
+ if (!g_error_matches (error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED))
+ g_warning ("Failed to add un Ubuntu Single Sign-on account: %s", error->message);
+ goto out;
+ }
+
+ ret = goa_object_get_account (goa_object);
+
+ out:
+ gtk_widget_destroy (dialog);
+ g_clear_object (&goa_object);
+ return ret;
+}
+
+static gboolean
+set_livepatch_enabled (GisLivepatchPage *page,
+ gboolean value)
+{
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ g_autoptr(GDBusProxy) proxy = NULL;
+ g_autoptr(GError) error = NULL;
+ GVariant *args;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, /* info */
+ "com.ubuntu.SoftwareProperties",
+ "/",
+ "com.ubuntu.SoftwareProperties",
+ NULL, /* cancellable */
+ &error);
+
+ if (proxy == NULL) {
+ g_warning ("Failed to get dbus proxy for com.ubuntu.SoftwareProperties: %s", error->message);
+ return FALSE;
+ }
+
+ if (value) {
+ args = g_variant_new ("(bs)", TRUE, priv->token);
+ } else {
+ args = g_variant_new ("(bs)", FALSE, "");
+ }
+
+ priv->waiting_for_livepatch_response = TRUE;
+ g_dbus_proxy_call (proxy,
+ "SetLivepatchEnabled",
+ args,
+ /* Fallback to interactive authorization if the meta action didn't work */
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
+ 1200000, /* 20 minutes timeout should be enough to install and enable livepatch */
+ NULL, /* cancellable */
+ on_livepatch_enabled,
+ page);
+
+ return TRUE;
+}
+
+static void
+on_livepatch_token_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ GoaPasswordBased *password_based = GOA_PASSWORD_BASED (source_object);
+ g_autoptr(GError) error = NULL;
+
+ if (!goa_password_based_call_get_password_finish (password_based, &priv->token, res, &error)) {
+ g_warning ("Failed to get livepatch token: %s", error->message);
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ return;
+ }
+
+ priv->user_current_choice = TRUE;
+ gtk_widget_set_visible (priv->message_box, TRUE);
+ if (set_livepatch_enabled (page, TRUE)) {
+ gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_SUCCESS_MESSAGE);
+ } else {
+ gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_FAILURE_MESSAGE);
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ }
+}
+
+static void
+on_account_id_stored (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!secret_password_store_finish (res, &error)) {
+ g_warning ("Failed to store account-id in the keyring: %s", error->message);
+ }
+}
+
+static void
+on_account_id_cleared (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!secret_password_clear_finish (res, &error)) {
+ g_warning ("Failed to clear account-id from the keyring: %s", error->message);
+ }
+}
+
+static void
+goa_account_store (GisLivepatchPage *page)
+{
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+
+ SecretSchema schema = {
+ "com.ubuntu.SotwareProperties",
+ SECRET_SCHEMA_NONE,
+ { { "key", SECRET_SCHEMA_ATTRIBUTE_STRING } }
+ };
+
+ if (priv->goa_account) {
+ secret_password_store (&schema,
+ NULL, /* collection */
+ "com.ubuntu.SoftwareProperties",
+ goa_account_get_id (priv->goa_account),
+ NULL, on_account_id_stored, NULL,
+ "key", "account-id", NULL);
+
+ } else {
+ secret_password_clear (&schema,
+ NULL, on_account_id_cleared, NULL,
+ "key", "account-id", NULL);
+ }
+}
+
+static void
+login_and_enable_livepatch (GisLivepatchPage *page)
+{
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ GoaObject *object = NULL;
+ GoaPasswordBased *password_based = NULL;
+
+ /* show the login dialog if needed */
+ if (priv->goa_account == NULL)
+ priv->goa_account = add_ubuntusso_account (page);
+
+ /* login dialog was dismissed */
+ if (priv->goa_account == NULL) {
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ return;
+ }
+
+ goa_account_store (page);
+
+ /* retrieve livepatch token */
+ object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (priv->goa_account)));
+ password_based = goa_object_peek_password_based (object);
+
+ goa_password_based_call_get_password (password_based,
+ "livepatch",
+ NULL /* cancellable */,
+ on_livepatch_token_ready,
+ page);
+
+}
+
+static void
+on_livepatch_permission_acquired (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ g_autoptr(GError) error = NULL;
+ gboolean allowed;
+
+ allowed = g_permission_acquire_finish (priv->permission, res, &error);
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to acquire permission: %s", error->message);
+ else
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ return;
+ }
+
+ if (allowed)
+ login_and_enable_livepatch (page);
+}
+
+static void
+on_setup_button_clicked (GtkButton *button,
+ gpointer data)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+
+ gtk_widget_set_sensitive (priv->setup_button, FALSE);
+
+ if (G_IS_PERMISSION (priv->permission) && g_permission_get_allowed (priv->permission)) {
+ login_and_enable_livepatch (page);
+ }
+ else if (G_IS_PERMISSION (priv->permission) && g_permission_get_can_acquire (priv->permission)) {
+ g_permission_acquire_async (priv->permission,
+ NULL,
+ on_livepatch_permission_acquired,
+ page);
+ } else {
+ g_warning ("Could not start the attempt to acquire the permission to enable Livepatch. Fallback to
per-app policy.");
+ login_and_enable_livepatch (page);
+ }
+}
+
+static void
+on_signout_button_clicked (GtkButton *button,
+ GisLivepatchPage *page)
+{
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+
+ /* disable livepatch */
+ priv->user_current_choice = FALSE;
+ if (!priv->waiting_for_livepatch_response && is_livepatch_enabled ())
+ set_livepatch_enabled (page, FALSE);
+
+ goa_account_store (page);
+ /* remove GoaAccount from system */
+ goa_account_call_remove (priv->goa_account, NULL, NULL, NULL);
+
+ /* reset the GUI */
+ gtk_widget_set_sensitive (priv->setup_button, TRUE);
+ gtk_widget_set_visible (priv->message_box, FALSE);
+
+ g_clear_object (&priv->goa_account);
+ g_clear_pointer (&priv->token, g_free);
+}
+
+static void
+gis_livepatch_page_constructed (GObject *object)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (object);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+ g_autoptr(GError) error = NULL;
+
+ G_OBJECT_CLASS (gis_livepatch_page_parent_class)->constructed (object);
+
+ gis_page_set_skippable (GIS_PAGE (page), TRUE);
+
+ priv->goa_client = goa_client_new_sync (NULL, &error);
+ priv->goa_account = NULL;
+ priv->token = NULL;
+ priv->waiting_for_livepatch_response = FALSE;
+ priv->user_current_choice = FALSE;
+
+ if (priv->goa_client == NULL) {
+ g_error ("Failed to get a GoaClient: %s", error->message);
+ return;
+ }
+
+ priv->permission = polkit_permission_new_sync ("com.ubuntu.welcome.livepatch", NULL, NULL, &error);
+ if (priv->permission == NULL) {
+ g_warning ("Could not get 'com.ubuntu.welcome.livepatch' permission: %s",
+ error->message);
+ }
+
+ g_signal_connect (priv->setup_button, "clicked",
+ G_CALLBACK (on_setup_button_clicked), page);
+
+ g_signal_connect (priv->signout_button, "clicked",
+ G_CALLBACK (on_signout_button_clicked), page);
+
+ gis_page_set_complete (GIS_PAGE (page), TRUE);
+ gtk_widget_show (GTK_WIDGET (page));
+}
+
+static void
+gis_livepatch_page_dispose (GObject *object)
+{
+ GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (object);
+ GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
+
+ g_clear_object (&priv->goa_client);
+ g_clear_object (&priv->goa_account);
+ g_clear_object (&priv->permission);
+ g_clear_pointer (&priv->token, g_free);
+
+ G_OBJECT_CLASS (gis_livepatch_page_parent_class)->dispose (object);
+}
+
+static void
+gis_livepatch_page_locale_changed (GisPage *page)
+{
+ gis_page_set_title (GIS_PAGE (page), _("Livepatch"));
+}
+
+static void
+gis_livepatch_page_class_init (GisLivepatchPageClass *klass)
+{
+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass),
"/org/gnome/initial-setup/gis-livepatch-page.ui");
+
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, setup_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, message_box);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, signout_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, message_label);
+
+ page_class->page_id = PAGE_ID;
+ page_class->locale_changed = gis_livepatch_page_locale_changed;
+ object_class->constructed = gis_livepatch_page_constructed;
+ object_class->dispose = gis_livepatch_page_dispose;
+}
+
+static void
+gis_livepatch_page_init (GisLivepatchPage *page)
+{
+ g_resources_register (livepatch_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (page));
+}
+
+void
+gis_prepare_livepatch_page (GisDriver *driver)
+{
+ if (is_livepatch_enabled ())
+ return;
+
+ gis_driver_add_page (driver,
+ g_object_new (GIS_TYPE_LIVEPATCH_PAGE,
+ "driver", driver,
+ NULL));
+}
diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h
b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h
new file mode 100644
index 0000000..eed18e5
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIS_LIVEPATCH_PAGE_H__
+#define __GIS_LIVEPATCH_PAGE_H__
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_LIVEPATCH_PAGE (gis_livepatch_page_get_type ())
+#define GIS_LIVEPATCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_LIVEPATCH_PAGE,
GisLivepatchPage))
+#define GIS_LIVEPATCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_LIVEPATCH_PAGE,
GisLivepatchPageClass))
+#define GIS_IS_LIVEPATCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_LIVEPATCH_PAGE))
+#define GIS_IS_LIVEPATCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_LIVEPATCH_PAGE))
+#define GIS_LIVEPATCH_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_LIVEPATCH_PAGE,
GisLivepatchPageClass))
+
+typedef struct _GisLivepatchPage GisLivepatchPage;
+typedef struct _GisLivepatchPageClass GisLivepatchPageClass;
+
+struct _GisLivepatchPage
+{
+ GisPage parent;
+};
+
+struct _GisLivepatchPageClass
+{
+ GisPageClass parent_class;
+};
+
+GType gis_livepatch_page_get_type (void);
+
+void gis_prepare_livepatch_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_LIVEPATCH_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
new file mode 100644
index 0000000..66b3fd0
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <template class="GisLivepatchPage" parent="GisPage">
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">40</property>
+ <property name="border_width">20</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes">Livepatch</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.8"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">3</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="icon_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="halign">start</property>
+ <property name="resource">/org/gnome/initial-setup/livepatch.svg</property>
+ <style>
+ <class name="dim-label" />
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="right_padding_label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="max-width-chars">50</property>
+ <property name="xalign">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Canonical Livepatch helps keep your computer
secure, by applying some updates that would normally require restarting.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="margin_top">18</property>
+ <property name="label" translatable="yes">Would you like to set up Livepatch now?</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="setup_button">
+ <property name="visible">True</property>
+ <property name="margin_top">18</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Set Up Livepatch…</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="message_box">
+ <property name="visible">False</property>
+ <property name="can_focus">False</property>
+ <property name="halign">fill</property>
+ <property name="orientation">horizontal</property>
+ <property name="margin_top">18</property>
+ <child>
+ <object class="GtkLabel" id="message_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="signout_button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Sign Out</property>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="icon_image"/>
+ <widget name="right_padding_label"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml
b/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml
new file mode 100644
index 0000000..d0490d9
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/initial-setup">
+ <file preprocess="xml-stripblanks" alias="gis-livepatch-page.ui">gis-livepatch-page.ui</file>
+ <file alias="livepatch.svg">livepatch.svg</file>
+ </gresource>
+</gresources>
+
diff --git a/gnome-initial-setup/pages/livepatch/livepatch.svg
b/gnome-initial-setup/pages/livepatch/livepatch.svg
new file mode 100644
index 0000000..c91aecb
--- /dev/null
+++ b/gnome-initial-setup/pages/livepatch/livepatch.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px"
y="0px" width="150px" height="150px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;"
xml:space="preserve"> <style type="text/css"> .st0{fill:#666666;} </style> <g> <path class="st0"
d="M73.7,168.4c0-69.7,56.7-126.3,126.3-126.3c69.7,0,126.3,56.7,126.3,126.3h42.1C368.4,75.6,292.9,0,200,0
C107.1,0,31.6,75.6,31.6,168.4v63.7h0C31.9,324.8,107.3,400,200,400c92.9,0,168.4-75.6,168.4-168.4v-21.1H73.7V168.4L73.7,168.4z
M200,242.1c17.4,0,31.6,14.1,31.6,31.6c0,13.7-8.8,25.4-21,29.7v54.4c0,5.8-4.7,10.5-10.5,10.5s-10.5-4.7-10.5-10.5v-54.4
c-12.3-4.3-21.1-16-21.1-29.8C168.4,256.3,182.6,242.1,200,242.1L200,242.1z"/> </g> </svg>
diff --git a/gnome-initial-setup/pages/privacy/gis-privacy-page.c
b/gnome-initial-setup/pages/privacy/gis-privacy-page.c
index f2af372..30e76e3 100644
--- a/gnome-initial-setup/pages/privacy/gis-privacy-page.c
+++ b/gnome-initial-setup/pages/privacy/gis-privacy-page.c
@@ -289,7 +289,7 @@ activate_link (GtkLabel *label,
static void
gis_privacy_page_locale_changed (GisPage *page)
{
- gis_page_set_title (GIS_PAGE (page), _("Privacy"));
+ gis_page_set_title (GIS_PAGE (page), _("Welcome to Ubuntu"));
}
static void
diff --git a/gnome-initial-setup/pages/ubuntu-changes/Makefile.am
b/gnome-initial-setup/pages/ubuntu-changes/Makefile.am
new file mode 100644
index 0000000..f7da724
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-changes/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libgisubuntu-changes.la
+
+BUILT_SOURCES =
+
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies
$(srcdir)/ubuntu-changes.gresource.xml)
+ubuntu-changes-resources.c: ubuntu-changes.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
+ubuntu-changes-resources.h: ubuntu-changes.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
+BUILT_SOURCES += ubuntu-changes-resources.c ubuntu-changes-resources.h
+
+libgisubuntu_changes_la_SOURCES = \
+ gis-ubuntu-changes-page.c gis-ubuntu-changes-page.h \
+ $(BUILT_SOURCES)
+
+libgisubuntu_changes_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.." -I "$(top_srcdir)" -I
"$(top_builddir)"
+libgisubuntu_changes_la_LIBADD = $(INITIAL_SETUP_LIBS)
+libgisubuntu_changes_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+EXTRA_DIST = ubuntu-changes.gresource.xml $(resource_files)
diff --git a/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.c
b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.c
new file mode 100644
index 0000000..e2c4dad
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.c
@@ -0,0 +1,106 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Ubuntu changes page {{{1 */
+
+#define PAGE_ID "ubuntu-changes"
+
+#include "config.h"
+#include "gis-ubuntu-changes-page.h"
+#include "ubuntu-changes-resources.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+struct _GisUbuntuChangesPagePrivate {
+ int dummy;
+};
+typedef struct _GisUbuntuChangesPagePrivate GisUbuntuChangesPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuChangesPage, gis_ubuntu_changes_page, GIS_TYPE_PAGE);
+
+static void
+gis_ubuntu_changes_page_constructed (GObject *object)
+{
+ GisUbuntuChangesPage *page = GIS_UBUNTU_CHANGES_PAGE (object);
+ GisUbuntuChangesPagePrivate *priv = gis_ubuntu_changes_page_get_instance_private (page);
+
+ G_OBJECT_CLASS (gis_ubuntu_changes_page_parent_class)->constructed (object);
+
+ gis_page_set_skippable (GIS_PAGE (page), TRUE);
+
+ gis_page_set_complete (GIS_PAGE (page), TRUE);
+ gtk_widget_show (GTK_WIDGET (page));
+}
+
+static void
+gis_ubuntu_changes_page_dispose (GObject *object)
+{
+ GisUbuntuChangesPage *page = GIS_UBUNTU_CHANGES_PAGE (object);
+ GisUbuntuChangesPagePrivate *priv = gis_ubuntu_changes_page_get_instance_private (page);
+
+ G_OBJECT_CLASS (gis_ubuntu_changes_page_parent_class)->dispose (object);
+}
+
+static gboolean
+activate_link (GtkLabel *label,
+ const gchar *uri,
+ GisUbuntuChangesPage *page)
+{
+ /* FIXME: Show changes */
+
+ return TRUE;
+}
+
+static void
+gis_ubuntu_changes_page_locale_changed (GisPage *page)
+{
+ gis_page_set_title (GIS_PAGE (page), _("What's new in Ubuntu"));
+}
+
+static void
+gis_ubuntu_changes_page_class_init (GisUbuntuChangesPageClass *klass)
+{
+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass),
"/org/gnome/initial-setup/gis-ubuntu-changes-page.ui");
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), activate_link);
+
+ page_class->page_id = PAGE_ID;
+ page_class->locale_changed = gis_ubuntu_changes_page_locale_changed;
+ object_class->constructed = gis_ubuntu_changes_page_constructed;
+ object_class->dispose = gis_ubuntu_changes_page_dispose;
+}
+
+static void
+gis_ubuntu_changes_page_init (GisUbuntuChangesPage *page)
+{
+ g_resources_register (ubuntu_changes_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (page));
+}
+
+void
+gis_prepare_ubuntu_changes_page (GisDriver *driver)
+{
+ gis_driver_add_page (driver,
+ g_object_new (GIS_TYPE_UBUNTU_CHANGES_PAGE,
+ "driver", driver,
+ NULL));
+}
diff --git a/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.h
b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.h
new file mode 100644
index 0000000..c42993b
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIS_UBUNTU_CHANGES_PAGE_H__
+#define __GIS_UBUNTU_CHANGES_PAGE_H__
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_UBUNTU_CHANGES_PAGE (gis_ubuntu_changes_page_get_type ())
+#define GIS_UBUNTU_CHANGES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GIS_TYPE_UBUNTU_CHANGES_PAGE, GisUbuntuChangesPage))
+#define GIS_UBUNTU_CHANGES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GIS_TYPE_UBUNTU_CHANGES_PAGE, GisUbuntuChangesPageClass))
+#define GIS_IS_UBUNTU_CHANGES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GIS_TYPE_UBUNTU_CHANGES_PAGE))
+#define GIS_IS_UBUNTU_CHANGES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GIS_TYPE_UBUNTU_CHANGES_PAGE))
+#define GIS_UBUNTU_CHANGES_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GIS_TYPE_UBUNTU_CHANGES_PAGE, GisUbuntuChangesPageClass))
+
+typedef struct _GisUbuntuChangesPage GisUbuntuChangesPage;
+typedef struct _GisUbuntuChangesPageClass GisUbuntuChangesPageClass;
+
+struct _GisUbuntuChangesPage
+{
+ GisPage parent;
+};
+
+struct _GisUbuntuChangesPageClass
+{
+ GisPageClass parent_class;
+};
+
+GType gis_ubuntu_changes_page_get_type (void);
+
+void gis_prepare_ubuntu_changes_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_UBUNTU_CHANGES_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.ui
b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.ui
new file mode 100644
index 0000000..c8b59eb
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.ui
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <template class="GisUbuntuChangesPage" parent="GisPage">
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">fill</property>
+ <property name="valign">fill</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">20</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes">What's new in Ubuntu</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.8"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="max-width-chars">50</property>
+ <property name="halign">start</property>
+ <property name="hexpand">False</property>
+ <property name="margin_top">18</property>
+ <property name="label" translatable="yes">Ubuntu 18.04 works differently from older
versions.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="changes_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">24</property>
+ <property name="resource">/org/gnome/initial-setup/ubuntu-changes.png</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="guide_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_top">18</property>
+ <property name="label" translatable="yes">If you like, you can read a <a href="">quick
guide to the new system</a>.</property>
+ <property name="use-markup">True</property>
+ <property name="wrap">True</property>
+ <signal name="activate-link" handler="activate_link"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.gresource.xml
b/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.gresource.xml
new file mode 100644
index 0000000..946b23b
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/initial-setup">
+ <file preprocess="xml-stripblanks" alias="gis-ubuntu-changes-page.ui">gis-ubuntu-changes-page.ui</file>
+ <file alias="ubuntu-changes.png">ubuntu-changes.png</file>
+ </gresource>
+</gresources>
+
diff --git a/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.png
b/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.png
new file mode 100644
index 0000000..4f8d296
Binary files /dev/null and b/gnome-initial-setup/pages/ubuntu-changes/ubuntu-changes.png differ
diff --git a/gnome-initial-setup/pages/ubuntu-report/Makefile.am
b/gnome-initial-setup/pages/ubuntu-report/Makefile.am
new file mode 100644
index 0000000..a769ba5
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libgisubuntu-report.la
+
+BUILT_SOURCES =
+
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --generate-dependencies
$(srcdir)/ubuntu-report.gresource.xml)
+ubuntu-report-resources.c: ubuntu-report.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
+ubuntu-report-resources.h: ubuntu-report.gresource.xml $(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-header $<
+BUILT_SOURCES += ubuntu-report-resources.c ubuntu-report-resources.h
+
+libgisubuntu_report_la_SOURCES = \
+ gis-ubuntu-report-page.c gis-ubuntu-report-page.h \
+ $(BUILT_SOURCES)
+
+libgisubuntu_report_la_CFLAGS = $(INITIAL_SETUP_CFLAGS) -I "$(srcdir)/../.." -I "$(top_srcdir)" -I
"$(top_builddir)"
+libgisubuntu_report_la_LIBADD = $(INITIAL_SETUP_LIBS)
+libgisubuntu_report_la_LDFLAGS = -export_dynamic -avoid-version -module -no-undefined
+
+EXTRA_DIST = ubuntu-report.gresource.xml $(resource_files)
diff --git a/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.c
b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.c
new file mode 100644
index 0000000..1e4f4ab
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.c
@@ -0,0 +1,243 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Ubuntu report page {{{1 */
+
+#define PAGE_ID "ubuntu-report"
+
+#include "config.h"
+#include "gis-ubuntu-report-page.h"
+#include "ubuntu-report-resources.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <libsysmetrics.h>
+
+struct _GisUbuntuReportPagePrivate {
+ GtkWidget *description_label;
+ GtkWidget *opt_in_radio;
+ gchar *report;
+};
+typedef struct _GisUbuntuReportPagePrivate GisUbuntuReportPagePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuReportPage, gis_ubuntu_report_page, GIS_TYPE_PAGE);
+
+static void
+gis_ubuntu_report_page_constructed (GObject *object)
+{
+ GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (object);
+ GisUbuntuReportPagePrivate *priv = gis_ubuntu_report_page_get_instance_private (page);
+
+ G_OBJECT_CLASS (gis_ubuntu_report_page_parent_class)->constructed (object);
+
+ gis_page_set_skippable (GIS_PAGE (page), TRUE);
+
+ gis_page_set_complete (GIS_PAGE (page), TRUE);
+ gtk_widget_show (GTK_WIDGET (page));
+}
+
+static void
+gis_ubuntu_report_page_dispose (GObject *object)
+{
+ GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (object);
+ GisUbuntuReportPagePrivate *priv = gis_ubuntu_report_page_get_instance_private (page);
+
+ g_free (priv->report);
+
+ G_OBJECT_CLASS (gis_ubuntu_report_page_parent_class)->dispose (object);
+}
+
+static void
+show_report (GtkButton *button, GisUbuntuReportPage *page)
+{
+ GisUbuntuReportPagePrivate *priv = gis_ubuntu_report_page_get_instance_private (page);
+ g_autofree char *error = NULL;
+ GtkWidget *dialog, *scroll, *text_view;
+
+ if (priv->report == NULL)
+ error = sysmetrics_collect (&priv->report);
+ if (error != NULL) {
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to get report information: %s"), error);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ dialog = gtk_dialog_new_with_buttons (_("Report"),
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
+ GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT |
+ GTK_DIALOG_USE_HEADER_BAR,
+ NULL, NULL);
+ gtk_widget_set_size_request (dialog, 800, 600);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scroll);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scroll, TRUE, TRUE, 0);
+
+ text_view = gtk_text_view_new ();
+ gtk_widget_show (text_view);
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)), priv->report, -1);
+ gtk_container_add (GTK_CONTAINER (scroll), text_view);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+show_legal (GtkButton *button, GisUbuntuReportPage *page)
+{
+ g_autofree gchar *data = NULL;
+ g_auto(GStrv) lines = NULL;
+ int i;
+ g_autofree gchar *privacy_policy_url = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!g_file_get_contents ("/etc/os-release", &data, NULL, &error)) {
+ g_warning ("Failed to get privacy policy URL from /etc/os-release: %s", error->message);
+ return;
+ }
+ lines = g_strsplit (data, "\n", -1);
+ for (i = 0; lines[i] != NULL; i++) {
+ g_auto(GStrv) tokens = NULL;
+ gchar *name, *value;
+
+ tokens = g_strsplit (lines[i], "=", 2);
+ if (g_strv_length (tokens) != 2)
+ continue;
+
+ name = g_strstrip (tokens[0]);
+ value = g_strstrip (tokens[1]);
+ if (value[0] == '"' && value[strlen (value) - 1] == '"') {
+ value[strlen (value) - 1] = '\0';
+ value = value + 1;
+ }
+
+ if (strcmp (name, "PRIVACY_POLICY_URL") == 0) {
+ privacy_policy_url = g_strdup (value);
+ break;
+ }
+ }
+ if (privacy_policy_url == NULL) {
+ g_warning ("PRIVACY_POLICY_URL not defined in /etc/os-release");
+ return;
+ }
+
+ if (!gtk_show_uri_on_window (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
+ privacy_policy_url,
+ GDK_CURRENT_TIME, &error)) {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to show privacy policy: %s"), error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+}
+
+static gboolean
+gis_ubuntu_report_page_apply (GisPage *page, GCancellable *cancellable)
+{
+ GisUbuntuReportPagePrivate *priv = gis_ubuntu_report_page_get_instance_private (GIS_UBUNTU_REPORT_PAGE
(page));
+ g_autofree char *error = NULL;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->opt_in_radio))) {
+ if (priv->report == NULL) {
+ error = sysmetrics_collect (&priv->report);
+ if (error != NULL) {
+ g_warning ("Failed to get report data: %s", error);
+ return FALSE;
+ }
+ }
+
+ error = sysmetrics_send_report (priv->report, FALSE, "");
+ if (error != NULL)
+ g_warning ("Failed to send report: %s", error);
+ } else {
+ error = sysmetrics_send_decline (FALSE, "");
+ if (error != NULL)
+ g_warning ("Failed to send decline: %s", error);
+ }
+
+ return FALSE;
+}
+
+static void
+gis_ubuntu_report_page_locale_changed (GisPage *page)
+{
+ GisUbuntuReportPagePrivate *priv = gis_ubuntu_report_page_get_instance_private (GIS_UBUNTU_REPORT_PAGE
(page));
+ g_autofree gchar *timezone_text = NULL;
+ g_autofree gchar *description_text = NULL;
+ g_autoptr(GError) error = NULL;
+
+ gis_page_set_title (GIS_PAGE (page), _("Help improve Ubuntu"));
+
+ if (!g_file_get_contents ("/etc/timezone", &timezone_text, NULL, &error)) {
+ g_warning ("Failed to get timezone from /etc/timezone: %s", error->message);
+ return;
+ }
+
+ description_text = g_strdup_printf (_("Ubuntu can report information that helps developers improve it. "
+ "This includes things like the computer model, "
+ "what software is installed, "
+ "and the approximate location you choose (%s)."), g_strstrip
(timezone_text));
+ gtk_label_set_label (GTK_LABEL (priv->description_label), description_text);
+}
+
+static void
+gis_ubuntu_report_page_class_init (GisUbuntuReportPageClass *klass)
+{
+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass),
"/org/gnome/initial-setup/gis-ubuntu-report-page.ui");
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage,
description_label);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage, opt_in_radio);
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), show_report);
+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), show_legal);
+
+ page_class->page_id = PAGE_ID;
+ page_class->apply = gis_ubuntu_report_page_apply;
+ page_class->locale_changed = gis_ubuntu_report_page_locale_changed;
+ object_class->constructed = gis_ubuntu_report_page_constructed;
+ object_class->dispose = gis_ubuntu_report_page_dispose;
+}
+
+static void
+gis_ubuntu_report_page_init (GisUbuntuReportPage *page)
+{
+ g_resources_register (ubuntu_report_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (page));
+}
+
+void
+gis_prepare_ubuntu_report_page (GisDriver *driver)
+{
+ gis_driver_add_page (driver,
+ g_object_new (GIS_TYPE_UBUNTU_REPORT_PAGE,
+ "driver", driver,
+ NULL));
+}
diff --git a/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.h
b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.h
new file mode 100644
index 0000000..1e5063f
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIS_UBUNTU_REPORT_PAGE_H__
+#define __GIS_UBUNTU_REPORT_PAGE_H__
+
+#include "gnome-initial-setup.h"
+
+G_BEGIN_DECLS
+
+#define GIS_TYPE_UBUNTU_REPORT_PAGE (gis_ubuntu_report_page_get_type ())
+#define GIS_UBUNTU_REPORT_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GIS_TYPE_UBUNTU_REPORT_PAGE, GisUbuntuReportPage))
+#define GIS_UBUNTU_REPORT_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GIS_TYPE_UBUNTU_REPORT_PAGE, GisUbuntuReportPageClass))
+#define GIS_IS_UBUNTU_REPORT_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GIS_TYPE_UBUNTU_REPORT_PAGE))
+#define GIS_IS_UBUNTU_REPORT_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GIS_TYPE_UBUNTU_REPORT_PAGE))
+#define GIS_UBUNTU_REPORT_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GIS_TYPE_UBUNTU_REPORT_PAGE, GisUbuntuReportPageClass))
+
+typedef struct _GisUbuntuReportPage GisUbuntuReportPage;
+typedef struct _GisUbuntuReportPageClass GisUbuntuReportPageClass;
+
+struct _GisUbuntuReportPage
+{
+ GisPage parent;
+};
+
+struct _GisUbuntuReportPageClass
+{
+ GisPageClass parent_class;
+};
+
+GType gis_ubuntu_report_page_get_type (void);
+
+void gis_prepare_ubuntu_report_page (GisDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GIS_UBUNTU_REPORT_PAGE_H__ */
diff --git a/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.ui
b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.ui
new file mode 100644
index 0000000..6190dd3
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.ui
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <template class="GisUbuntuReportPage" parent="GisPage">
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">40</property>
+ <property name="border_width">20</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes">Help improve Ubuntu</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.8"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">3</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="icon_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="halign">start</property>
+ <property name="resource">/org/gnome/initial-setup/ubuntu-report.svg</property>
+ <style>
+ <class name="dim-label" />
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="right_padding_label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="max-width-chars">50</property>
+ <property name="halign">center</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="orientation">horizontal</property>
+ <property name="margin_top">18</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show the First Report</property>
+ <signal name="clicked" handler="show_report"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Legal notice</property>
+ <signal name="clicked" handler="show_legal"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="question_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">18</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Would you like to send this
information?</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="opt_in_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">18</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Yes, send system info to Canonical</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="opt_out_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">6</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">No, don't send system info</property>
+ <property name="group">opt_in_radio</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="footer_label">
+ <property name="visible">False</property> <!-- Disabled for now as setting doesn't exist -
remove if we decide it's not necessary -->
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">You can change your mind later in Settings ->
Privacy -> Diagnostics.</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <property name="margin_bottom">18</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="icon_image"/>
+ <widget name="right_padding_label"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="description_label"/>
+ <widget name="question_box"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.gresource.xml
b/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.gresource.xml
new file mode 100644
index 0000000..6b28415
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/initial-setup">
+ <file preprocess="xml-stripblanks" alias="gis-ubuntu-report-page.ui">gis-ubuntu-report-page.ui</file>
+ <file alias="ubuntu-report.svg">ubuntu-report.svg</file>
+ </gresource>
+</gresources>
+
diff --git a/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.svg
b/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.svg
new file mode 100644
index 0000000..fe3811c
--- /dev/null
+++ b/gnome-initial-setup/pages/ubuntu-report/ubuntu-report.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px"
y="0px" width="150px" height="150px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;"
xml:space="preserve"> <style type="text/css"> .st0{fill:#666666;} </style> <g> <path class="st0"
d="M301.4,372.4C271.7,390,237,400,200,400C89.5,400,0,310.5,0,200C0,89.5,89.5,0,200,0c110.5,0,200,89.5,200,200
c0,36.4-9.7,70.6-26.8,100l0,0L400,400l-100-26.8L301.4,372.4L301.4,372.4z"/> </g> </svg>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7af03af..5acf040 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,3 +1,4 @@
+data/com.ubuntu.welcome.policy.in
data/gnome-initial-setup.desktop.in.in
data/gnome-initial-setup-first-login.desktop.in.in
gnome-initial-setup/gis-assistant.c
@@ -41,3 +42,11 @@ gnome-initial-setup/pages/timezone/gis-timezone-page.c
[type: gettext/glade]gnome-initial-setup/pages/timezone/gis-timezone-page.ui
gnome-initial-setup/pages/software/gis-software-page.c
[type: gettext/glade]gnome-initial-setup/pages/software/gis-software-page.ui
+gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.c
+[type: gettext/glade]gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.ui
+gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
+[type: gettext/glade]gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
+gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.c
+[type: gettext/glade]gnome-initial-setup/pages/ubuntu-changes/gis-ubuntu-changes-page.ui
+gnome-initial-setup/pages/apps/gis-apps-page.c
+[type: gettext/glade]gnome-initial-setup/pages/apps/gis-apps-page.ui
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]