summaryrefslogtreecommitdiffstats
path: root/panels/online-accounts
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
commitae1c76ff830d146d41e88d6fba724c0a54bce868 (patch)
tree3c354bec95af07be35fc71a4b738268496f1a1c4 /panels/online-accounts
parentInitial commit. (diff)
downloadgnome-control-center-upstream.tar.xz
gnome-control-center-upstream.zip
Adding upstream version 1:43.6.upstream/1%43.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--panels/online-accounts/cc-online-account-provider-row.c135
-rw-r--r--panels/online-accounts/cc-online-account-provider-row.h32
-rw-r--r--panels/online-accounts/cc-online-account-provider-row.ui12
-rw-r--r--panels/online-accounts/cc-online-account-row.c140
-rw-r--r--panels/online-accounts/cc-online-account-row.h35
-rw-r--r--panels/online-accounts/cc-online-account-row.ui17
-rw-r--r--panels/online-accounts/cc-online-accounts-panel.c947
-rw-r--r--panels/online-accounts/cc-online-accounts-panel.h31
-rw-r--r--panels/online-accounts/cc-online-accounts-panel.ui115
-rw-r--r--panels/online-accounts/gnome-control-center-goa-helper.c505
-rw-r--r--panels/online-accounts/gnome-online-accounts-panel.desktop.in.in21
-rw-r--r--panels/online-accounts/icons/meson.build4
-rw-r--r--panels/online-accounts/icons/scalable/org.gnome.Settings-online-accounts-symbolic.svg4
-rw-r--r--panels/online-accounts/meson.build84
-rw-r--r--panels/online-accounts/online-accounts.css6
-rw-r--r--panels/online-accounts/online-accounts.gresource.xml9
16 files changed, 2097 insertions, 0 deletions
diff --git a/panels/online-accounts/cc-online-account-provider-row.c b/panels/online-accounts/cc-online-account-provider-row.c
new file mode 100644
index 0000000..d581d15
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-provider-row.c
@@ -0,0 +1,135 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2020 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "cc-online-account-provider-row.h"
+#include "cc-online-accounts-resources.h"
+
+struct _CcOnlineAccountProviderRow
+{
+ AdwActionRow parent;
+
+ GtkImage *icon_image;
+
+ GVariant *provider;
+};
+
+G_DEFINE_TYPE (CcOnlineAccountProviderRow, cc_online_account_provider_row, ADW_TYPE_ACTION_ROW)
+
+static gboolean
+is_gicon_symbolic (GtkWidget *widget,
+ GIcon *icon)
+{
+ g_autoptr(GtkIconPaintable) icon_paintable = NULL;
+ GtkIconTheme *icon_theme;
+
+ icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+ icon_paintable = gtk_icon_theme_lookup_by_gicon (icon_theme,
+ icon,
+ 32,
+ gtk_widget_get_scale_factor (widget),
+ gtk_widget_get_direction (widget),
+ 0);
+
+ return icon_paintable && gtk_icon_paintable_is_symbolic (icon_paintable);
+}
+
+static void
+cc_online_account_provider_row_dispose (GObject *object)
+{
+ CcOnlineAccountProviderRow *self = CC_ONLINE_ACCOUNT_PROVIDER_ROW (object);
+
+ g_clear_pointer (&self->provider, g_variant_unref);
+
+ G_OBJECT_CLASS (cc_online_account_provider_row_parent_class)->dispose (object);
+}
+
+static void
+cc_online_account_provider_row_class_init (CcOnlineAccountProviderRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = cc_online_account_provider_row_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/online-accounts/cc-online-account-provider-row.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountProviderRow, icon_image);
+}
+
+static void
+cc_online_account_provider_row_init (CcOnlineAccountProviderRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+CcOnlineAccountProviderRow *
+cc_online_account_provider_row_new (GVariant *provider)
+{
+ CcOnlineAccountProviderRow *self;
+ g_autoptr(GIcon) icon = NULL;
+ g_autofree gchar *name = NULL;
+
+ self = g_object_new (cc_online_account_provider_row_get_type (), NULL);
+
+ if (provider == NULL)
+ {
+ icon = g_themed_icon_new_with_default_fallbacks ("goa-account");
+ name = g_strdup (C_("Online Account", "Other"));
+ }
+ else
+ {
+ g_autoptr(GVariant) icon_variant = NULL;
+
+ self->provider = g_variant_ref (provider);
+
+ g_variant_get (provider, "(ssviu)",
+ NULL,
+ &name,
+ &icon_variant,
+ NULL,
+ NULL);
+
+ icon = g_icon_deserialize (icon_variant);
+ }
+
+ gtk_image_set_from_gicon (self->icon_image, icon);
+ if (is_gicon_symbolic (GTK_WIDGET (self), icon))
+ {
+ gtk_image_set_icon_size (self->icon_image, GTK_ICON_SIZE_NORMAL);
+ gtk_widget_add_css_class (GTK_WIDGET (self->icon_image), "symbolic-circular");
+ }
+ else
+ {
+ gtk_image_set_icon_size (self->icon_image, GTK_ICON_SIZE_LARGE);
+ gtk_widget_add_css_class (GTK_WIDGET (self->icon_image), "lowres-icon");
+ }
+
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self), name);
+
+ return self;
+}
+
+GVariant *
+cc_online_account_provider_row_get_provider (CcOnlineAccountProviderRow *self)
+{
+ g_return_val_if_fail (CC_IS_ONLINE_ACCOUNT_PROVIDER_ROW (self), NULL);
+ return self->provider;
+}
diff --git a/panels/online-accounts/cc-online-account-provider-row.h b/panels/online-accounts/cc-online-account-provider-row.h
new file mode 100644
index 0000000..6f75a5b
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-provider-row.h
@@ -0,0 +1,32 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2020 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <adwaita.h>
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE (CcOnlineAccountProviderRow, cc_online_account_provider_row, CC, ONLINE_ACCOUNT_PROVIDER_ROW, AdwActionRow)
+
+CcOnlineAccountProviderRow *cc_online_account_provider_row_new (GVariant *provider);
+
+GVariant *cc_online_account_provider_row_get_provider (CcOnlineAccountProviderRow *row);
+
+G_END_DECLS
diff --git a/panels/online-accounts/cc-online-account-provider-row.ui b/panels/online-accounts/cc-online-account-provider-row.ui
new file mode 100644
index 0000000..6c51df7
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-provider-row.ui
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="CcOnlineAccountProviderRow" parent="AdwActionRow">
+ <property name="activatable">True</property>
+ <child type="prefix">
+ <object class="GtkImage" id="icon_image">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/online-accounts/cc-online-account-row.c b/panels/online-accounts/cc-online-account-row.c
new file mode 100644
index 0000000..1406a3d
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-row.c
@@ -0,0 +1,140 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2020 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "cc-online-account-row.h"
+#include "cc-online-accounts-resources.h"
+
+struct _CcOnlineAccountRow
+{
+ AdwActionRow parent;
+
+ GtkImage *icon_image;
+ GtkImage *warning_image;
+
+ GoaObject *object;
+};
+
+G_DEFINE_TYPE (CcOnlineAccountRow, cc_online_account_row, ADW_TYPE_ACTION_ROW)
+
+static gboolean
+is_gicon_symbolic (GtkWidget *widget,
+ GIcon *icon)
+{
+ g_autoptr(GtkIconPaintable) icon_paintable = NULL;
+ GtkIconTheme *icon_theme;
+
+ icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+ icon_paintable = gtk_icon_theme_lookup_by_gicon (icon_theme,
+ icon,
+ 32,
+ gtk_widget_get_scale_factor (widget),
+ gtk_widget_get_direction (widget),
+ 0);
+
+ return icon_paintable && gtk_icon_paintable_is_symbolic (icon_paintable);
+}
+
+static void
+cc_online_account_row_dispose (GObject *object)
+{
+ CcOnlineAccountRow *self = CC_ONLINE_ACCOUNT_ROW (object);
+
+ g_clear_object (&self->object);
+
+ G_OBJECT_CLASS (cc_online_account_row_parent_class)->dispose (object);
+}
+
+static void
+cc_online_account_row_class_init (CcOnlineAccountRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = cc_online_account_row_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/online-accounts/cc-online-account-row.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountRow, icon_image);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountRow, warning_image);
+}
+
+static void
+cc_online_account_row_init (CcOnlineAccountRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+CcOnlineAccountRow *
+cc_online_account_row_new (GoaObject *object)
+{
+ CcOnlineAccountRow *self;
+ GoaAccount *account;
+ g_autoptr(GIcon) gicon = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_object_new (cc_online_account_row_get_type (), NULL);
+
+ self->object = g_object_ref (object);
+
+ account = goa_object_peek_account (object);
+
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self),
+ goa_account_get_provider_name (account));
+ adw_action_row_set_subtitle (ADW_ACTION_ROW (self),
+ goa_account_get_presentation_identity (account));
+
+ gicon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error);
+ if (error != NULL)
+ {
+ g_warning ("Error creating GIcon for account: %s (%s, %d)",
+ error->message,
+ g_quark_to_string (error->domain),
+ error->code);
+ }
+ else
+ {
+ gtk_image_set_from_gicon (self->icon_image, gicon);
+
+ if (is_gicon_symbolic (GTK_WIDGET (self), gicon))
+ {
+ gtk_image_set_icon_size (self->icon_image, GTK_ICON_SIZE_NORMAL);
+ gtk_widget_add_css_class (GTK_WIDGET (self->icon_image), "symbolic-circular");
+ }
+ else
+ {
+ gtk_image_set_icon_size (self->icon_image, GTK_ICON_SIZE_LARGE);
+ gtk_widget_add_css_class (GTK_WIDGET (self->icon_image), "lowres-icon");
+ }
+ }
+
+ g_object_bind_property (account, "attention-needed",
+ self->warning_image, "visible",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+ return self;
+}
+
+GoaObject *
+cc_online_account_row_get_object (CcOnlineAccountRow *self)
+{
+ g_return_val_if_fail (CC_IS_ONLINE_ACCOUNT_ROW (self), NULL);
+ return self->object;
+}
diff --git a/panels/online-accounts/cc-online-account-row.h b/panels/online-accounts/cc-online-account-row.h
new file mode 100644
index 0000000..aa0df4b
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-row.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2020 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <adwaita.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE (CcOnlineAccountRow, cc_online_account_row, CC, ONLINE_ACCOUNT_ROW, AdwActionRow)
+
+CcOnlineAccountRow *cc_online_account_row_new (GoaObject *object);
+
+GoaObject *cc_online_account_row_get_object (CcOnlineAccountRow *row);
+
+G_END_DECLS
diff --git a/panels/online-accounts/cc-online-account-row.ui b/panels/online-accounts/cc-online-account-row.ui
new file mode 100644
index 0000000..15dee53
--- /dev/null
+++ b/panels/online-accounts/cc-online-account-row.ui
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="CcOnlineAccountRow" parent="AdwActionRow">
+ <property name="activatable">True</property>
+ <child type="prefix">
+ <object class="GtkImage" id="icon_image">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <child type="suffix">
+ <object class="GtkImage" id="warning_image">
+ <property name="icon-name">dialog-warning-symbolic</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/online-accounts/cc-online-accounts-panel.c b/panels/online-accounts/cc-online-accounts-panel.c
new file mode 100644
index 0000000..f4d1a92
--- /dev/null
+++ b/panels/online-accounts/cc-online-accounts-panel.c
@@ -0,0 +1,947 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 - 2017 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+#include "cc-online-accounts-panel.h"
+#include "cc-online-account-provider-row.h"
+#include "cc-online-account-row.h"
+#include "cc-online-accounts-resources.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+
+struct _CcOnlineAccountsPanel
+{
+ CcPanel parent_instance;
+
+ GtkFrame *accounts_frame;
+ GtkListBox *accounts_listbox;
+ GtkWidget *close_notification_button;
+ GtkDialog *edit_account_dialog;
+ GtkHeaderBar *edit_account_headerbar;
+ GtkBox *editor_box;
+ GtkLabel *notification_label;
+ GtkRevealer *notification_revealer;
+ GtkLabel *offline_label;
+ GtkListBox *providers_listbox;
+ GtkButton *remove_account_button;
+ GtkBox *accounts_vbox;
+
+ GoaClient *client;
+ GoaObject *active_object;
+ GoaObject *removed_object;
+
+ guint remove_account_timeout_id;
+ gchar *window_export_handle;
+};
+
+static gboolean remove_account_timeout_cb (gpointer user_data);
+
+CC_PANEL_REGISTER (CcOnlineAccountsPanel, cc_online_accounts_panel);
+
+enum {
+ PROP_0,
+ PROP_PARAMETERS
+};
+
+/* Rows methods */
+
+typedef void (*RowForAccountCallback) (CcOnlineAccountsPanel *self, GtkWidget *row, GList *other_rows);
+
+static void
+hide_row_for_account_cb (CcOnlineAccountsPanel *self,
+ GtkWidget *row,
+ GList *other_rows)
+{
+ gtk_widget_hide (row);
+ gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
+}
+
+static void
+remove_row_for_account_cb (CcOnlineAccountsPanel *self,
+ GtkWidget *row,
+ GList *other_rows)
+{
+ gtk_list_box_remove (self->accounts_listbox, row);
+ gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
+}
+
+static void
+show_row_for_account_cb (CcOnlineAccountsPanel *self,
+ GtkWidget *row,
+ GList *other_rows)
+{
+ gtk_widget_show (row);
+ gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+}
+
+static void
+modify_row_for_account (CcOnlineAccountsPanel *self,
+ GoaObject *object,
+ RowForAccountCallback callback)
+{
+ GtkWidget *child;
+ GList *children = NULL;
+ GList *l;
+
+ for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ children = g_list_prepend (children, child);
+ }
+
+ children = g_list_reverse (children);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ GoaObject *row_object;
+
+ row_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (l->data));
+ if (row_object == object)
+ {
+ GtkWidget *row = GTK_WIDGET (l->data);
+
+ children = g_list_remove_link (children, l);
+ callback (self, row, children);
+ g_list_free (l);
+ break;
+ }
+ }
+
+ g_list_free (children);
+}
+
+/* Auxiliary methods */
+
+G_GNUC_NULL_TERMINATED
+static char *
+run_goa_helper_sync (const char *command,
+ ...)
+{
+ g_autoptr(GPtrArray) argv = NULL;
+ g_autofree char *output = NULL;
+ g_autoptr(GError) error = NULL;
+ const char *param;
+ va_list args;
+ int status;
+
+ argv = g_ptr_array_new_with_free_func (g_free);
+ g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-control-center-goa-helper"));
+ g_ptr_array_add (argv, g_strdup (command));
+
+ va_start (args, command);
+ while ((param = va_arg (args, const char*)) != NULL)
+ g_ptr_array_add (argv, g_strdup (param));
+ va_end (args);
+
+ g_ptr_array_add (argv, NULL);
+
+ if (!g_spawn_sync (NULL,
+ (char **) argv->pdata,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ &output,
+ NULL,
+ &status,
+ &error))
+ {
+ g_warning ("Failed to run online accounts helper: %s", error->message);
+ return NULL;
+ }
+
+ if (!g_spawn_check_wait_status (status, NULL))
+ return NULL;
+
+ if (output == NULL || *output == '\0')
+ return NULL;
+
+ return g_steal_pointer (&output);
+}
+
+static void
+run_goa_helper_in_thread_func (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autofree char *output = NULL;
+ g_autoptr(GError) error = NULL;
+ GPtrArray *argv = task_data;
+ int status;
+
+ g_spawn_sync (NULL,
+ (char **) argv->pdata,
+ NULL, 0, NULL, NULL,
+ &output,
+ NULL,
+ &status,
+ &error);
+
+ if (error)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ if (!g_spawn_check_wait_status (status, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ g_task_return_pointer (task, g_steal_pointer (&output), g_free);
+}
+
+static void
+run_goa_helper_async (const gchar *command,
+ const gchar *param,
+ const gchar *window_handle,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GPtrArray) argv = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ argv = g_ptr_array_new_with_free_func (g_free);
+ g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-control-center-goa-helper"));
+ g_ptr_array_add (argv, g_strdup (command));
+ g_ptr_array_add (argv, g_strdup (param));
+ g_ptr_array_add (argv, g_strdup (window_handle));
+ g_ptr_array_add (argv, NULL);
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (task, run_goa_helper_async);
+ g_task_set_task_data (task, g_steal_pointer (&argv), (GDestroyNotify) g_ptr_array_unref);
+ g_task_run_in_thread (task, run_goa_helper_in_thread_func);
+}
+
+static void
+cancel_notification_timeout (CcOnlineAccountsPanel *self)
+{
+ g_clear_handle_id (&self->remove_account_timeout_id, g_source_remove);
+ self->removed_object = NULL;
+}
+
+static void
+start_remove_account_timeout (CcOnlineAccountsPanel *self)
+{
+ GoaAccount *account;
+ g_autofree gchar *id = NULL;
+ g_autofree gchar *label = NULL;
+
+ if (self->active_object == NULL)
+ return;
+
+ if (self->removed_object != NULL)
+ gtk_widget_activate (self->close_notification_button);
+
+ self->removed_object = g_steal_pointer (&self->active_object);
+
+ account = goa_object_peek_account (self->removed_object);
+ id = g_strdup_printf ("<b>%s</b>", goa_account_get_presentation_identity (account));
+ /* Translators: The %s is the username (eg., debarshi.ray@gmail.com
+ * or rishi).
+ */
+ label = g_strdup_printf (_("%s removed"), id);
+ gtk_label_set_markup (self->notification_label, label);
+ gtk_revealer_set_reveal_child (self->notification_revealer, TRUE);
+
+ modify_row_for_account (self, self->removed_object, hide_row_for_account_cb);
+ self->remove_account_timeout_id = g_timeout_add_seconds (10, remove_account_timeout_cb, self);
+}
+
+static void
+on_show_account_finish_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
+ g_autofree char *output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ output = g_task_propagate_pointer (G_TASK (result), &error);
+
+ if (error)
+ {
+ g_warning ("Error showing account: %s", error->message);
+ return;
+ }
+
+ if (g_strcmp0 (output, "remove") == 0)
+ start_remove_account_timeout (self);
+
+ self->active_object = NULL;
+}
+
+static void
+show_account (CcOnlineAccountsPanel *self,
+ GoaObject *object)
+{
+ GoaAccount *account;
+
+ if (!self->window_export_handle)
+ return;
+
+ self->active_object = g_object_ref (object);
+
+ account = goa_object_peek_account (object);
+ run_goa_helper_async ("show-account",
+ goa_account_get_id (account),
+ self->window_export_handle,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ on_show_account_finish_cb,
+ self);
+}
+
+static void
+on_create_account_finish_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
+ g_autofree char *new_account_id = NULL;
+ g_autoptr(GoaObject) object = NULL;
+ g_autoptr(GError) error = NULL;
+
+ new_account_id = g_task_propagate_pointer (G_TASK (result), &error);
+
+ if (error)
+ {
+ g_warning ("Error showing account: %s", error->message);
+ return;
+ }
+
+ if (new_account_id)
+ object = goa_client_lookup_by_id (self->client, new_account_id);
+
+ if (object)
+ show_account (self, object);
+}
+
+static void
+create_account (CcOnlineAccountsPanel *self,
+ GVariant *provider)
+{
+ g_autofree char *provider_type = NULL;
+
+ if (!self->window_export_handle)
+ return;
+
+ g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL);
+
+ run_goa_helper_async ("create-account",
+ provider_type,
+ self->window_export_handle,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ on_create_account_finish_cb,
+ self);
+}
+
+static void
+add_provider_row (CcOnlineAccountsPanel *self,
+ GVariant *provider)
+{
+ CcOnlineAccountProviderRow *row;
+
+ row = cc_online_account_provider_row_new (provider);
+
+ gtk_widget_show (GTK_WIDGET (row));
+ gtk_list_box_append (self->providers_listbox, GTK_WIDGET (row));
+}
+
+static void
+list_providers (CcOnlineAccountsPanel *self)
+{
+ g_autoptr(GVariant) providers_variant = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *providers = NULL;
+ GVariantIter iter;
+ GVariant *provider;
+
+ providers = run_goa_helper_sync ("list-providers", NULL);
+
+ if (!providers)
+ return;
+
+ providers_variant = g_variant_parse (G_VARIANT_TYPE ("a(ssviu)"),
+ providers,
+ NULL,
+ NULL,
+ &error);
+
+ if (error)
+ {
+ g_warning ("Error listing providers: %s", error->message);
+ return;
+ }
+
+ g_variant_iter_init (&iter, providers_variant);
+
+ while ((provider = g_variant_iter_next_value (&iter)))
+ add_provider_row (self, provider);
+}
+
+static void
+add_account (CcOnlineAccountsPanel *self,
+ GoaObject *object)
+{
+ CcOnlineAccountRow *row;
+
+ row = cc_online_account_row_new (object);
+
+ /* Add to the listbox */
+ gtk_list_box_append (self->accounts_listbox, GTK_WIDGET (row));
+ gtk_widget_show (GTK_WIDGET (self->accounts_frame));
+}
+
+static void
+fill_accounts_listbox (CcOnlineAccountsPanel *self)
+{
+ g_autolist(GoaAccount) accounts = NULL;
+ GList *l;
+
+ accounts = goa_client_get_accounts (self->client);
+
+ for (l = accounts; l != NULL; l = l->next)
+ add_account (self, l->data);
+}
+
+#ifdef GDK_WINDOWING_WAYLAND
+static void
+wayland_window_exported_cb (GdkToplevel *toplevel,
+ const char *handle,
+ gpointer data)
+
+{
+ CcOnlineAccountsPanel *self = data;
+
+ self->window_export_handle = g_strdup_printf ("wayland:%s", handle);
+}
+#endif
+
+static void
+export_window_handle (CcOnlineAccountsPanel *self)
+{
+ GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+ {
+ GdkSurface *surface = gtk_native_get_surface (native);
+ guint32 xid = (guint32) gdk_x11_surface_get_xid (surface);
+
+ self->window_export_handle = g_strdup_printf ("x11:%x", xid);
+ }
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+ {
+ GdkSurface *surface = gtk_native_get_surface (native);
+
+ gdk_wayland_toplevel_export_handle (GDK_TOPLEVEL (surface),
+ wayland_window_exported_cb,
+ self,
+ NULL);
+ }
+#endif
+}
+
+static void
+unexport_window_handle (CcOnlineAccountsPanel *self)
+{
+ if (!self->window_export_handle)
+ return;
+
+#ifdef GDK_WINDOWING_WAYLAND
+ GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
+
+ if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native))))
+ {
+ GdkSurface *surface = gtk_native_get_surface (native);
+ gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (surface));
+ }
+#endif
+}
+
+static void
+select_account_by_id (CcOnlineAccountsPanel *self,
+ const gchar *account_id)
+{
+ GtkWidget *child;
+
+ for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ GoaAccount *account;
+ GoaObject *row_object;
+
+ row_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (child));
+ account = goa_object_peek_account (row_object);
+
+ if (g_strcmp0 (goa_account_get_id (account), account_id) == 0)
+ {
+ show_account (self, row_object);
+ break;
+ }
+ }
+}
+
+static void
+command_add (CcOnlineAccountsPanel *self,
+ GVariant *parameters)
+{
+ const gchar *provider_name = NULL;
+ g_autoptr(GVariant) v = NULL;
+
+ g_assert (self != NULL);
+ g_assert (parameters != NULL);
+
+ switch (g_variant_n_children (parameters))
+ {
+ case 2:
+ g_variant_get_child (parameters, 1, "v", &v);
+ if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
+ provider_name = g_variant_get_string (v, NULL);
+ else
+ g_warning ("Wrong type for the second argument (provider name) GVariant, expected 's' but got '%s'",
+ (gchar *)g_variant_get_type (v));
+ break;
+
+ default:
+ g_warning ("Unexpected parameters found, ignore request");
+ return;
+ }
+
+ if (provider_name != NULL)
+ {
+ GtkWidget *child;
+ GVariant *provider;
+
+ for (child = gtk_widget_get_first_child (GTK_WIDGET (self->providers_listbox));
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ g_autofree gchar *provider_type = NULL;
+
+ provider = cc_online_account_provider_row_get_provider (CC_ONLINE_ACCOUNT_PROVIDER_ROW (child));
+ g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL);
+
+ if (g_strcmp0 (provider_type, provider_name) == 0)
+ break;
+ }
+
+ if (child == NULL)
+ {
+ g_warning ("Unable to get a provider for type '%s'", provider_name);
+ return;
+ }
+
+ create_account (self, provider);
+ }
+}
+
+static void
+load_custom_css (void)
+{
+ g_autoptr(GtkCssProvider) provider = NULL;
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/online-accounts/online-accounts.css");
+ gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+}
+
+/* Callbacks */
+
+static gint
+sort_accounts_func (GtkListBoxRow *a,
+ GtkListBoxRow *b,
+ gpointer user_data)
+{
+ GoaAccount *a_account, *b_account;
+ GoaObject *a_object, *b_object;
+
+ a_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (a));
+ a_account = goa_object_peek_account (a_object);
+
+ b_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (b));
+ b_account = goa_object_peek_account (b_object);
+
+ return g_strcmp0 (goa_account_get_id (a_account), goa_account_get_id (b_account));
+}
+
+static gint
+sort_providers_func (GtkListBoxRow *a,
+ GtkListBoxRow *b,
+ gpointer user_data)
+{
+ GVariant *a_provider, *b_provider;
+ gboolean a_branded, b_branded;
+ gint a_features, b_features;
+
+ a_provider = cc_online_account_provider_row_get_provider (CC_ONLINE_ACCOUNT_PROVIDER_ROW (a));
+ b_provider = cc_online_account_provider_row_get_provider (CC_ONLINE_ACCOUNT_PROVIDER_ROW (b));
+
+ g_variant_get (a_provider, "(ssviu)", NULL, NULL, NULL, &a_features, NULL);
+ g_variant_get (b_provider, "(ssviu)", NULL, NULL, NULL, &b_features, NULL);
+
+ /* FIXME: this needs to go away once libgoa-backend is ported to GTK4 */
+#define FEATURE_BRANDED (1 << 1)
+
+ a_branded = (a_features & FEATURE_BRANDED) != 0;
+ b_branded = (a_features & FEATURE_BRANDED) != 0;
+
+#undef FEATURE_BRANDED
+
+ if (a_branded != b_branded)
+ {
+ if (a_branded)
+ return -1;
+ else
+ return 1;
+ }
+
+ return gtk_list_box_row_get_index (b) - gtk_list_box_row_get_index (a);
+}
+
+static void
+on_account_added_cb (GoaClient *client,
+ GoaObject *object,
+ CcOnlineAccountsPanel *self)
+{
+ add_account (self, object);
+}
+
+static void
+on_account_changed_cb (GoaClient *client,
+ GoaObject *object,
+ CcOnlineAccountsPanel *self)
+{
+ if (self->active_object == object)
+ show_account (self, self->active_object);
+}
+
+static void
+on_account_removed_cb (GoaClient *client,
+ GoaObject *object,
+ CcOnlineAccountsPanel *self)
+{
+ modify_row_for_account (self, object, remove_row_for_account_cb);
+}
+
+static void
+on_accounts_listbox_row_activated (CcOnlineAccountsPanel *self,
+ GtkListBoxRow *activated_row)
+{
+ GoaObject *object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (activated_row));
+
+ show_account (self, object);
+}
+
+static void
+on_client_remove_account_finish_cb (GoaAccount *account,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(CcOnlineAccountsPanel) self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
+ g_autoptr(GError) error = NULL;
+
+ goa_account_call_remove_finish (account, res, &error);
+
+ if (error)
+ {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new (GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)))),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Error removing account"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s",
+ error->message);
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+}
+
+static void
+on_notification_closed_cb (GtkButton *button,
+ CcOnlineAccountsPanel *self)
+{
+ goa_account_call_remove (goa_object_peek_account (self->removed_object),
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ (GAsyncReadyCallback) on_client_remove_account_finish_cb,
+ g_object_ref (self));
+
+ gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
+
+ cancel_notification_timeout (self);
+ self->removed_object = NULL;
+}
+
+static void
+on_undo_button_clicked_cb (GtkButton *button,
+ CcOnlineAccountsPanel *self)
+{
+ /* Simply show the account row and hide the notification */
+ modify_row_for_account (self, self->removed_object, show_row_for_account_cb);
+ gtk_revealer_set_reveal_child (self->notification_revealer, FALSE);
+
+ cancel_notification_timeout (self);
+ self->removed_object = NULL;
+}
+
+static void
+on_provider_row_activated_cb (CcOnlineAccountsPanel *self,
+ GtkListBoxRow *activated_row)
+{
+ GVariant *provider = cc_online_account_provider_row_get_provider (CC_ONLINE_ACCOUNT_PROVIDER_ROW (activated_row));
+
+ create_account (self, provider);
+}
+
+static gboolean
+remove_account_timeout_cb (gpointer user_data)
+{
+ CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
+
+ gtk_widget_activate (self->close_notification_button);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* CcPanel overrides */
+
+static const char *
+cc_online_accounts_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/accounts";
+}
+
+/* GtkWidget overrides */
+
+static void
+cc_online_accounts_panel_realize (GtkWidget *widget)
+{
+ GTK_WIDGET_CLASS (cc_online_accounts_panel_parent_class)->realize (widget);
+
+ export_window_handle (CC_ONLINE_ACCOUNTS_PANEL (widget));
+}
+
+static void
+cc_online_accounts_panel_unrealize (GtkWidget *widget)
+{
+ unexport_window_handle (CC_ONLINE_ACCOUNTS_PANEL (widget));
+
+ GTK_WIDGET_CLASS (cc_online_accounts_panel_parent_class)->unrealize (widget);
+}
+
+/* GObject overrides */
+
+static void
+cc_online_accounts_panel_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_PARAMETERS:
+ {
+ GVariant *parameters;
+ g_autoptr(GVariant) v = NULL;
+ const gchar *first_arg = NULL;
+
+ parameters = g_value_get_variant (value);
+ if (parameters == NULL)
+ return;
+
+ if (g_variant_n_children (parameters) > 0)
+ {
+ g_variant_get_child (parameters, 0, "v", &v);
+ if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
+ first_arg = g_variant_get_string (v, NULL);
+ else
+ g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
+ (gchar *)g_variant_get_type (v));
+ }
+
+ if (g_strcmp0 (first_arg, "add") == 0)
+ command_add (CC_ONLINE_ACCOUNTS_PANEL (object), parameters);
+ else if (first_arg != NULL)
+ select_account_by_id (CC_ONLINE_ACCOUNTS_PANEL (object), first_arg);
+
+ return;
+ }
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cc_online_accounts_panel_constructed (GObject *object)
+{
+ CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (object);
+
+ G_OBJECT_CLASS (cc_online_accounts_panel_parent_class)->constructed (object);
+
+ list_providers (self);
+}
+
+static void
+cc_online_accounts_panel_finalize (GObject *object)
+{
+ CcOnlineAccountsPanel *panel = CC_ONLINE_ACCOUNTS_PANEL (object);
+
+ if (panel->removed_object != NULL)
+ {
+ g_autoptr(GError) error = NULL;
+ goa_account_call_remove_sync (goa_object_peek_account (panel->removed_object),
+ NULL, /* GCancellable */
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("Error removing account: %s (%s, %d)",
+ error->message,
+ g_quark_to_string (error->domain),
+ error->code);
+ }
+ }
+
+ g_clear_object (&panel->client);
+
+ G_OBJECT_CLASS (cc_online_accounts_panel_parent_class)->finalize (object);
+}
+
+static void
+cc_online_accounts_panel_class_init (CcOnlineAccountsPanelClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ panel_class->get_help_uri = cc_online_accounts_panel_get_help_uri;
+
+ object_class->set_property = cc_online_accounts_panel_set_property;
+ object_class->finalize = cc_online_accounts_panel_finalize;
+ object_class->constructed = cc_online_accounts_panel_constructed;
+
+ widget_class->realize = cc_online_accounts_panel_realize;
+ widget_class->unrealize = cc_online_accounts_panel_unrealize;
+
+ g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/online-accounts/cc-online-accounts-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, accounts_frame);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, accounts_listbox);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, close_notification_button);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, notification_label);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, notification_revealer);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, offline_label);
+ gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, providers_listbox);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_accounts_listbox_row_activated);
+ gtk_widget_class_bind_template_callback (widget_class, on_notification_closed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_provider_row_activated_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_undo_button_clicked_cb);
+}
+
+static void
+cc_online_accounts_panel_init (CcOnlineAccountsPanel *self)
+{
+ g_autoptr(GError) error = NULL;
+ GNetworkMonitor *monitor;
+
+ g_resources_register (cc_online_accounts_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_list_box_set_sort_func (self->accounts_listbox,
+ sort_accounts_func,
+ self,
+ NULL);
+
+ gtk_list_box_set_sort_func (self->providers_listbox,
+ sort_providers_func,
+ self,
+ NULL);
+
+ monitor = g_network_monitor_get_default();
+ g_object_bind_property (monitor,
+ "network-available",
+ self->offline_label,
+ "visible",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ g_object_bind_property (monitor,
+ "network-available",
+ self->providers_listbox,
+ "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /* TODO: probably want to avoid _sync() ... */
+ self->client = goa_client_new_sync (cc_panel_get_cancellable (CC_PANEL (self)), &error);
+ if (self->client == NULL)
+ {
+ g_warning ("Error getting a GoaClient: %s (%s, %d)",
+ error->message, g_quark_to_string (error->domain), error->code);
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ return;
+ }
+
+ g_signal_connect (self->client,
+ "account-added",
+ G_CALLBACK (on_account_added_cb),
+ self);
+
+ g_signal_connect (self->client,
+ "account-changed",
+ G_CALLBACK (on_account_changed_cb),
+ self);
+
+ g_signal_connect (self->client,
+ "account-removed",
+ G_CALLBACK (on_account_removed_cb),
+ self);
+
+ fill_accounts_listbox (self);
+ load_custom_css ();
+}
diff --git a/panels/online-accounts/cc-online-accounts-panel.h b/panels/online-accounts/cc-online-accounts-panel.h
new file mode 100644
index 0000000..e7404a6
--- /dev/null
+++ b/panels/online-accounts/cc-online-accounts-panel.h
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#pragma once
+
+#include <shell/cc-panel.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_ONLINE_ACCOUTS_PANEL (cc_online_accounts_panel_get_type ())
+
+G_DECLARE_FINAL_TYPE (CcOnlineAccountsPanel, cc_online_accounts_panel, CC, ONLINE_ACCOUNTS_PANEL, CcPanel)
+
+G_END_DECLS
diff --git a/panels/online-accounts/cc-online-accounts-panel.ui b/panels/online-accounts/cc-online-accounts-panel.ui
new file mode 100644
index 0000000..87a0232
--- /dev/null
+++ b/panels/online-accounts/cc-online-accounts-panel.ui
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="CcOnlineAccountsPanel" parent="CcPanel">
+ <child type="content">
+ <object class="GtkOverlay">
+ <child type="overlay">
+ <object class="GtkRevealer" id="notification_revealer">
+ <property name="halign">center</property>
+ <property name="valign">start</property>
+ <property name="transition_type">slide-down</property>
+ <child>
+ <object class="GtkFrame">
+ <child>
+ <object class="GtkBox">
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="notification_label">
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="undo_button">
+ <property name="label" translatable="yes">Undo</property>
+ <signal name="clicked" handler="on_undo_button_clicked_cb" object="CcOnlineAccountsPanel" swapped="no" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_notification_button">
+ <property name="icon-name">window-close-symbolic</property>
+ <accessibility>
+ <property name="label" translatable="yes">Close the notification</property>
+ </accessibility>
+ <signal name="clicked" handler="on_notification_closed_cb" object="CcOnlineAccountsPanel" swapped="no" />
+ <style>
+ <class name="flat" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="app-notification" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesPage">
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="GtkLabel" id="accounts_label">
+ <property name="wrap">True</property>
+ <property name="justify">center</property>
+ <property name="label" translatable="yes">Connect to your data in the cloud</property>
+ <style>
+ <class name="title-1" />
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="offline_label">
+ <property name="margin-top">24</property>
+ <property name="wrap">True</property>
+ <property name="label" translatable="yes">No internet connection — connect to set up new online accounts</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup" id="accounts_frame">
+ <property name="visible">False</property>
+ <child>
+ <object class="GtkListBox" id="accounts_listbox">
+ <property name="hexpand">True</property>
+ <property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_accounts_listbox_row_activated" object="CcOnlineAccountsPanel" swapped="yes" />
+ <style>
+ <class name="boxed-list" />
+ </style>
+ <accessibility>
+ <relation name="labelled-by">accounts_label</relation>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Add an account</property>
+ <child>
+ <object class="GtkListBox" id="providers_listbox">
+ <property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_provider_row_activated_cb" object="CcOnlineAccountsPanel" swapped="yes" />
+ <style>
+ <class name="boxed-list" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+</interface>
diff --git a/panels/online-accounts/gnome-control-center-goa-helper.c b/panels/online-accounts/gnome-control-center-goa-helper.c
new file mode 100644
index 0000000..1b40c84
--- /dev/null
+++ b/panels/online-accounts/gnome-control-center-goa-helper.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2022 Endless OS Foundation, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ * Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
+#include <goabackend/goabackend.h>
+
+#ifdef HAVE_GTK_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef HAVE_GTK_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+static GdkDisplay *
+get_wayland_display (void)
+{
+ static GdkDisplay *wayland_display = NULL;
+
+ if (wayland_display)
+ return wayland_display;
+
+ gdk_set_allowed_backends ("wayland");
+ wayland_display = gdk_display_open (NULL);
+ gdk_set_allowed_backends (NULL);
+ if (!wayland_display)
+ g_warning ("Failed to open Wayland display");
+
+ return wayland_display;
+}
+
+static GdkDisplay *
+get_x11_display (void)
+{
+ static GdkDisplay *x11_display = NULL;
+
+ if (x11_display)
+ return x11_display;
+
+ gdk_set_allowed_backends ("x11");
+ x11_display = gdk_display_open (NULL);
+ gdk_set_allowed_backends (NULL);
+ if (!x11_display)
+ g_warning ("Failed to open X11 display");
+
+ return x11_display;
+}
+
+static void
+set_external_parent_from_handle (GtkApplication *application,
+ GtkWindow *dialog,
+ const char *handle_str)
+{
+ GdkDisplay *display;
+ GtkWindow *fake_parent;
+ GdkScreen *screen;
+
+#ifdef HAVE_GTK_X11
+ {
+ const char *x11_prefix = "x11:";
+ if (g_str_has_prefix (handle_str, x11_prefix))
+ {
+ display = get_x11_display ();
+ if (!display)
+ {
+ g_warning ("No X display connection, ignoring X11 parent");
+ return;
+ }
+ }
+ }
+#endif
+#ifdef HAVE_GTK_WAYLAND
+ {
+ const char *wayland_prefix = "wayland:";
+
+ if (g_str_has_prefix (handle_str, wayland_prefix))
+ {
+ display = get_wayland_display ();
+ if (!display)
+ {
+ g_warning ("No Wayland display connection, ignoring Wayland parent");
+ return;
+ }
+ }
+ }
+#endif
+
+ screen = gdk_display_get_default_screen (gdk_display_get_default ());
+ fake_parent = g_object_new (GTK_TYPE_APPLICATION_WINDOW,
+ "application", application,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "screen", screen,
+ NULL);
+ g_object_ref_sink (fake_parent);
+
+ gtk_window_set_transient_for (dialog, GTK_WINDOW (fake_parent));
+ gtk_window_set_modal (dialog, TRUE);
+ gtk_widget_realize (GTK_WIDGET (dialog));
+
+#ifdef HAVE_GTK_X11
+ {
+ const char *x11_prefix = "x11:";
+ if (g_str_has_prefix (handle_str, x11_prefix))
+ {
+ GdkWindow *foreign_gdk_window;
+ int xid;
+
+ errno = 0;
+ xid = strtol (handle_str + strlen (x11_prefix), NULL, 16);
+ if (errno != 0)
+ {
+ g_warning ("Failed to reference external X11 window, invalid XID %s", handle_str);
+ return;
+ }
+
+ foreign_gdk_window = gdk_x11_window_foreign_new_for_display (display, xid);
+ if (!foreign_gdk_window)
+ {
+ g_warning ("Failed to create foreign window for XID %d", xid);
+ return;
+ }
+
+ gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)),
+ foreign_gdk_window);
+ }
+ }
+#endif
+#ifdef HAVE_GTK_WAYLAND
+ {
+ const char *wayland_prefix = "wayland:";
+
+ if (g_str_has_prefix (handle_str, wayland_prefix))
+ {
+ const char *wayland_handle_str = handle_str + strlen (wayland_prefix);
+
+ if (!gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (GTK_WIDGET (dialog)),
+ (char *) wayland_handle_str))
+ {
+ g_warning ("Failed to set window transient for external parent");
+ return;
+ }
+ }
+ }
+#endif
+
+ gtk_window_present (dialog);
+}
+
+/* create-account */
+
+static void
+on_application_activate_create_account_cb (GtkApplication *application,
+ char **argv)
+{
+ g_autoptr(GoaProvider) provider = NULL;
+ g_autoptr(GoaClient) client = NULL;
+ g_autoptr(GError) error = NULL;
+ GoaAccount *account;
+ GtkWidget *content_area;
+ GtkWidget *dialog;
+ GoaObject *object;
+
+ client = goa_client_new_sync (NULL, &error);
+ if (error)
+ {
+ g_printerr ("Error retrieving online accounts client");
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+
+ /* Find the provider with a matching type */
+ provider = goa_provider_get_for_provider_type (argv[2]);
+ if (!provider)
+ {
+ g_printerr ("Provider type not supported");
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ dialog = g_object_new (GTK_TYPE_DIALOG,
+ "use-header-bar", 1,
+ "default-width", 500,
+ "default-height", 350,
+ NULL);
+ g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application);
+ set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ object = goa_provider_add_account (provider,
+ client,
+ GTK_DIALOG (dialog),
+ GTK_BOX (content_area),
+ &error);
+ if (error)
+ {
+ g_printerr ("Failed to create account: %s", error->message);
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ account = goa_object_peek_account (object);
+ g_print ("%s", goa_account_get_id (account));
+}
+
+static int
+create_account (int argc,
+ char **argv)
+{
+ g_autoptr(GtkApplication) application = NULL;
+
+ gtk_init (&argc, &argv);
+
+ if (argc != 4)
+ {
+ g_printerr ("Not enough arguments");
+ return EXIT_FAILURE;
+ }
+
+ application = gtk_application_new ("org.gnome.Settings.GoaHelper",
+ G_APPLICATION_FLAGS_NONE);
+ g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_create_account_cb), argv);
+
+ return g_application_run (G_APPLICATION (application), 0, NULL);
+}
+
+/* list-providers */
+
+typedef struct {
+ GMainLoop *mainloop;
+ GList *providers;
+ GError *error;
+} GetAllProvidersData;
+
+static void
+get_all_providers_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autolist(GoaProvider) providers = NULL;
+ GetAllProvidersData *data;
+
+ data = user_data;
+
+ goa_provider_get_all_finish (&providers, res, &data->error);
+ if (data->error)
+ goto out;
+
+ data->providers = g_steal_pointer (&providers);
+
+out:
+ g_main_loop_quit (data->mainloop);
+}
+
+static GList *
+get_all_providers (GError **error)
+{
+ GetAllProvidersData data = (GetAllProvidersData) {
+ .mainloop = g_main_loop_new (NULL, FALSE),
+ .providers = NULL,
+ .error = NULL,
+ };
+
+ goa_provider_get_all (get_all_providers_cb, &data);
+
+ g_main_loop_run (data.mainloop);
+
+ if (data.error)
+ g_propagate_error (error, data.error);
+
+ return data.providers;
+}
+
+static int
+list_providers (int argc,
+ char **argv)
+{
+ g_autofree char *serialized_result = NULL;
+ g_autolist(GoaProvider) providers = NULL;
+ g_autoptr(GVariant) result = NULL;
+ g_autoptr(GError) error = NULL;
+ GVariantBuilder b;
+ GList *l;
+
+ providers = get_all_providers (&error);
+
+ if (error)
+ {
+ g_printerr ("%s", error->message);
+ return EXIT_FAILURE;
+ }
+
+ g_variant_builder_init (&b, G_VARIANT_TYPE ("a(ssviu)"));
+ for (l = providers; l; l = l->next)
+ {
+ GoaProvider *provider = l->data;
+ g_autofree char *name = NULL;
+ g_autoptr(GVariant) icon_variant = NULL;
+ g_autoptr(GIcon) icon = NULL;
+
+ name = goa_provider_get_provider_name (provider, NULL);
+ icon = goa_provider_get_provider_icon (provider, NULL);
+ icon_variant = g_icon_serialize (icon);
+
+ g_variant_builder_add (&b, "(ssviu)",
+ goa_provider_get_provider_type (provider),
+ name,
+ icon_variant,
+ goa_provider_get_provider_features (provider),
+ goa_provider_get_credentials_generation (provider));
+ }
+ result = g_variant_builder_end (&b);
+
+ serialized_result = g_variant_print (result, TRUE);
+ g_print ("%s", serialized_result);
+
+ return EXIT_SUCCESS;
+}
+
+/* show-account */
+
+static void
+on_remove_button_clicked_cb (GtkButton *button,
+ GApplication *application)
+{
+ g_print ("remove");
+ g_application_quit (application);
+}
+
+static void
+on_application_activate_show_account_cb (GtkApplication *application,
+ char **argv)
+{
+ g_autoptr(GoaProvider) provider = NULL;
+ g_autoptr(GoaObject) object = NULL;
+ g_autoptr(GoaClient) client = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *title = NULL;
+ GoaAccount *account;
+ GtkWidget *content_area;
+ GtkWidget *button;
+ GtkWidget *dialog;
+ GtkWidget *box;
+ const char *provider_type;
+
+ client = goa_client_new_sync (NULL, &error);
+ if (error)
+ {
+ g_printerr ("Error retrieving online accounts client");
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ object = goa_client_lookup_by_id (client, argv[2]);
+ if (!object)
+ {
+ g_printerr ("Online account does not exist");
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ /* Find the provider with a matching type */
+ account = goa_object_get_account (object);
+ provider_type = goa_account_get_provider_type (account);
+ provider = goa_provider_get_for_provider_type (provider_type);
+ if (!provider)
+ {
+ g_printerr ("Provider type not supported");
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ dialog = g_object_new (GTK_TYPE_DIALOG,
+ "use-header-bar", 1,
+ NULL);
+ /* Keep account alive so that the switches are still bound to it */
+ g_object_set_data_full (G_OBJECT (dialog), "goa-account", account, g_object_unref);
+ g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application);
+ set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 42);
+ gtk_widget_set_margin_bottom (box, 24);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+ gtk_container_add (GTK_CONTAINER (content_area), box);
+
+ goa_provider_show_account (provider,
+ client,
+ object,
+ GTK_BOX (box),
+ NULL,
+ NULL);
+
+ /*
+ * The above call doesn't set any widgets to visible, so we have to do that.
+ * https://gitlab.gnome.org/GNOME/gnome-online-accounts/issues/56
+ */
+ gtk_widget_show_all (box);
+
+ /* translators: This is the title of the "Show Account" dialog. The
+ * %s is the name of the provider. e.g., 'Google'. */
+ title = g_strdup_printf (_("%s Account"), goa_account_get_provider_name (account));
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+ button = gtk_button_new_with_label (_("Remove Account"));
+ gtk_widget_set_margin_start (box, 24);
+ gtk_widget_set_margin_end (box, 24);
+ gtk_widget_set_halign (button, GTK_ALIGN_END);
+ gtk_widget_set_valign (button, GTK_ALIGN_END);
+ gtk_widget_set_visible (button, !goa_account_get_is_locked (account));
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
+ gtk_container_add (GTK_CONTAINER (box), button);
+ g_signal_connect (button, "clicked", G_CALLBACK (on_remove_button_clicked_cb), application);
+}
+
+static int
+show_account (int argc,
+ char **argv)
+{
+ g_autoptr(GtkApplication) application = NULL;
+
+ gtk_init (&argc, &argv);
+
+ if (argc != 4)
+ {
+ g_printerr ("Not enough arguments");
+ return EXIT_FAILURE;
+ }
+
+ application = gtk_application_new ("org.gnome.Settings.GoaHelper",
+ G_APPLICATION_FLAGS_NONE);
+ g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_show_account_cb), argv);
+
+ return g_application_run (G_APPLICATION (application), 0, NULL);
+}
+
+struct {
+ const char *command_name;
+ int (*command_func) (int argc,
+ char **argv);
+} commands[] = {
+ { "create-account", create_account, },
+ { "list-providers", list_providers, },
+ { "show-account", show_account, },
+};
+
+
+static void
+log_handler (const gchar *domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ g_printerr ("%s: %s", domain, message);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ gsize i;
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ if (argc < 2)
+ return EXIT_FAILURE;
+
+ g_log_set_default_handler (log_handler, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (commands); i++)
+ {
+ if (g_strcmp0 (commands[i].command_name, argv[1]) == 0)
+ return commands[i].command_func (argc, argv);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/panels/online-accounts/gnome-online-accounts-panel.desktop.in.in b/panels/online-accounts/gnome-online-accounts-panel.desktop.in.in
new file mode 100644
index 0000000..fe77f8e
--- /dev/null
+++ b/panels/online-accounts/gnome-online-accounts-panel.desktop.in.in
@@ -0,0 +1,21 @@
+[Desktop Entry]
+Name=Online Accounts
+Comment=Connect to your online accounts and decide what to use them for
+Exec=gnome-control-center online-accounts
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=org.gnome.Settings-online-accounts-symbolic
+Terminal=false
+Type=Application
+NoDisplay=true
+StartupNotify=true
+Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-AccountSettings;
+OnlyShowIn=GNOME;Unity;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-control-center
+X-GNOME-Bugzilla-Component=Online Accounts
+X-GNOME-Bugzilla-Version=@VERSION@
+# Translators: Search terms to find the Online Accounts panel.
+# Do NOT translate or localize the semicolons!
+# The list MUST also end with a semicolon!
+# For ReadItLater and Pocket, see http://en.wikipedia.org/wiki/Pocket_(application)
+Keywords=Google;Facebook;Twitter;Yahoo;Web;Online;Chat;Calendar;Mail;Contact;ownCloud;Kerberos;IMAP;SMTP;Pocket;ReadItLater;
diff --git a/panels/online-accounts/icons/meson.build b/panels/online-accounts/icons/meson.build
new file mode 100644
index 0000000..9482f1c
--- /dev/null
+++ b/panels/online-accounts/icons/meson.build
@@ -0,0 +1,4 @@
+install_data(
+ 'scalable/org.gnome.Settings-online-accounts-symbolic.svg',
+ install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps')
+)
diff --git a/panels/online-accounts/icons/scalable/org.gnome.Settings-online-accounts-symbolic.svg b/panels/online-accounts/icons/scalable/org.gnome.Settings-online-accounts-symbolic.svg
new file mode 100644
index 0000000..6a2a682
--- /dev/null
+++ b/panels/online-accounts/icons/scalable/org.gnome.Settings-online-accounts-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 7.664062 0.0078125 c -2.613281 0.1093755 -5.003906 1.4882815 -6.410156 3.6953125 c -1.90625 3 -1.609375 6.898437 0.734375 9.574219 c 2.347657 2.667968 6.175781 3.472656 9.398438 1.96875 c 0.5 -0.234375 0.714843 -0.828125 0.484375 -1.328125 c -0.234375 -0.5 -0.828125 -0.71875 -1.332032 -0.484375 c -2.414062 1.132812 -5.289062 0.53125 -7.046874 -1.476563 c -1.761719 -2.003906 -1.988282 -4.933593 -0.550782 -7.179687 c 1.429688 -2.253906 4.175782 -3.289063 6.738282 -2.539063 c 2.5625 0.746094 4.324218 3.09375 4.320312 5.761719 v 0.039062 v 0.960938 c 0 0.359375 -0.1875 0.683594 -0.5 0.863281 c -0.308594 0.179688 -0.6875 0.179688 -1 0 c -0.308594 -0.175781 -0.5 -0.507812 -0.5 -0.863281 v -1 c 0 -2.199219 -1.800781 -4 -4 -4 c -2.195312 0 -4 1.800781 -4 4 c 0 2.195312 1.804688 4 4 4 c 1.046875 0 1.992188 -0.417969 2.707031 -1.078125 c 0.222657 0.265625 0.484375 0.496094 0.792969 0.675781 c 0.929688 0.535156 2.074219 0.535156 3 0 c 0.929688 -0.539062 1.5 -1.527344 1.5 -2.597656 v -1 c 0 -3.558594 -2.347656 -6.683594 -5.761719 -7.683594 c -0.835937 -0.2421872 -1.703125 -0.347656 -2.574219 -0.3085935 z m 0.335938 5.9921875 c 1.117188 0 2 0.882812 2 2 c 0 1.113281 -0.882812 2 -2 2 c -1.113281 0 -2 -0.886719 -2 -2 c 0 -1.117188 0.886719 -2 2 -2 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/panels/online-accounts/meson.build b/panels/online-accounts/meson.build
new file mode 100644
index 0000000..9d1b918
--- /dev/null
+++ b/panels/online-accounts/meson.build
@@ -0,0 +1,84 @@
+panels_list += cappletname
+desktop = 'gnome-@0@-panel.desktop'.format(cappletname)
+
+desktop_in = configure_file(
+ input: desktop + '.in.in',
+ output: desktop + '.in',
+ configuration: desktop_conf
+)
+
+i18n.merge_file(
+ type: 'desktop',
+ input: desktop_in,
+ output: desktop,
+ po_dir: po_dir,
+ install: true,
+ install_dir: control_center_desktopdir
+)
+
+cflags += [
+ '-DLIBEXECDIR="@0@"'.format(control_center_libexecdir),
+]
+
+sources = files(
+ 'cc-online-account-provider-row.c',
+ 'cc-online-account-row.c',
+ 'cc-online-accounts-panel.c',
+)
+
+resource_data = files(
+ 'cc-online-account-provider-row.ui',
+ 'cc-online-account-row.ui',
+ 'cc-online-accounts-panel.ui',
+ 'online-accounts.css',
+)
+
+sources += gnome.compile_resources(
+ 'cc-' + cappletname + '-resources',
+ cappletname + '.gresource.xml',
+ c_name: 'cc_' + cappletname.underscorify(),
+ dependencies: resource_data,
+ export: true
+)
+
+deps = common_deps + [
+ goa_dep,
+]
+
+panels_libs += static_library(
+ cappletname,
+ sources: sources,
+ include_directories: [ top_inc, common_inc ],
+ dependencies: deps,
+ c_args: cflags
+)
+
+goa_helper_deps = [
+ dependency('goa-backend-1.0', version: goa_req_version),
+]
+
+goa_helper_cflags = cflags + [
+ '-DGNOMELOCALEDIR="@0@"'.format(control_center_localedir),
+]
+
+gtk_x11_dep = dependency('gtk+-x11-3.0', required: false)
+if gtk_x11_dep.found()
+ goa_helper_cflags += ['-DHAVE_GTK_X11']
+endif
+
+gtk_wayland_dep = dependency('gtk+-wayland-3.0', required: false)
+if gtk_wayland_dep.found()
+ goa_helper_cflags += ['-DHAVE_GTK_WAYLAND']
+endif
+
+executable(
+ 'gnome-control-center-goa-helper',
+ 'gnome-control-center-goa-helper.c',
+ include_directories: [ top_inc ],
+ dependencies: goa_helper_deps,
+ c_args: goa_helper_cflags,
+ install: true,
+ install_dir: control_center_libexecdir,
+)
+
+subdir('icons')
diff --git a/panels/online-accounts/online-accounts.css b/panels/online-accounts/online-accounts.css
new file mode 100644
index 0000000..08a60b6
--- /dev/null
+++ b/panels/online-accounts/online-accounts.css
@@ -0,0 +1,6 @@
+image.symbolic-circular {
+ background-color: alpha(currentColor, 0.08);
+ min-width: 32px;
+ min-height: 32px;
+ border-radius: 50%;
+}
diff --git a/panels/online-accounts/online-accounts.gresource.xml b/panels/online-accounts/online-accounts.gresource.xml
new file mode 100644
index 0000000..0e452b6
--- /dev/null
+++ b/panels/online-accounts/online-accounts.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/control-center/online-accounts">
+ <file preprocess="xml-stripblanks">cc-online-account-provider-row.ui</file>
+ <file preprocess="xml-stripblanks">cc-online-account-row.ui</file>
+ <file preprocess="xml-stripblanks">cc-online-accounts-panel.ui</file>
+ <file>online-accounts.css</file>
+ </gresource>
+</gresources>