[xdg-desktop-portal-gnome/gbsneto/more-portals: 2/4] Implement the Account portal




commit 751da3d52db827d4ca43d2b6733f326c58d292b7
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Dec 14 10:38:27 2021 -0300

    Implement the Account portal
    
    Closes https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/issues/7

 data/gnome.portal                          |   2 +-
 data/org.freedesktop.Accounts.User.xml     | 815 +++++++++++++++++++++++++++++
 data/org.freedesktop.Accounts.xml          | 228 ++++++++
 src/account.c                              | 260 +++++++++
 src/account.h                              |  26 +
 src/accountdialog.c                        | 252 +++++++++
 src/accountdialog.h                        |  17 +
 src/accountdialog.ui                       | 100 ++++
 src/meson.build                            |   5 +
 src/xdg-desktop-portal-gnome.c             |   7 +
 src/xdg-desktop-portal-gnome.gresource.xml |   1 +
 11 files changed, 1712 insertions(+), 1 deletion(-)
---
diff --git a/data/gnome.portal b/data/gnome.portal
index d47d251..38a6f85 100644
--- a/data/gnome.portal
+++ b/data/gnome.portal
@@ -1,4 +1,4 @@
 [portal]
 DBusName=org.freedesktop.impl.portal.desktop.gnome
-Interfaces=org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Print;
+Interfaces=org.freedesktop.impl.portal.Account;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Print;
 UseIn=gnome
diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
new file mode 100644
index 0000000..a6a8b5e
--- /dev/null
+++ b/data/org.freedesktop.Accounts.User.xml
@@ -0,0 +1,815 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"; >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
+  <interface name="org.freedesktop.Accounts.User">
+
+  <method name="SetUserName">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="name" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new username.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users username. Note that it is usually not allowed
+          to have multiple users with the same username.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the username of any user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetRealName">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="name" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new name, typically in the form "Firstname Lastname".
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users real name.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own name</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the name of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetEmail">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="email" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new email address.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users email address.
+        </doc:para>
+        <doc:para>
+          Note that setting an email address in the AccountsService is
+          not the same as configuring a mail client. Mail clients might
+          default to email address that is configured here, though.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own email address</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the email address of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetLanguage">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="language" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new language, as a locale specification like "de_DE.UTF-8".
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users language.
+        </doc:para>
+        <doc:para>
+          The expectation is that display managers will start the
+          users session with this locale.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own language</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the language of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetXSession">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/>
+    <arg name="x_session" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new xsession to start (e.g. "gnome")
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users x session.
+        </doc:para>
+        <doc:para>
+          The expectation is that display managers will log the user in to this
+          specified session, if available.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own language</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the language of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+   </doc:doc>
+  </method>
+
+  <method name="SetLocation">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="location" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new location as a freeform string.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users location.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own location</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the location of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetHomeDirectory">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="homedir" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new homedir as an absolute path.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users home directory.
+        </doc:para>
+        <doc:para>
+          Note that changing the users home directory moves all the content
+          from the old location to the new one, and is potentially an
+          expensive operation.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the home directory of a user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetShell">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="shell" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The new user shell.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users shell.
+        </doc:para>
+        <doc:para>
+          Note that setting the shell to a non-allowed program may
+          prevent the user from logging in.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the shell of a user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetIconFile">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="filename" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The absolute filename of a png file to use as the users icon.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users icon.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own icon</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the icon of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetLocked">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="locked" direction="in" type="b">
+      <doc:doc>
+        <doc:summary>
+          Whether to lock or unlock the users account.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Locks or unlocks a users account.
+        </doc:para>
+        <doc:para>
+          Locking an account prevents the user from logging in.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To lock or unlock user accounts</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetAccountType">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="accountType" direction="in" type="i">
+      <doc:doc>
+        <doc:summary>
+          The new account type, encoded as an integer:
+          <doc:list>
+            <doc:item>
+              <doc:term>0</doc:term>
+              <doc:definition>Standard user</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>1</doc:term>
+              <doc:definition>Administrator</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Changes the users account type.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change an account type</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetPasswordMode">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="mode" direction="in" type="i">
+      <doc:doc>
+        <doc:summary>
+          The new password mode, encoded as an integer:
+          <doc:list>
+            <doc:item>
+              <doc:term>0</doc:term>
+              <doc:definition>Regular password</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>1</doc:term>
+              <doc:definition>Password must be set at next login</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>2</doc:term>
+              <doc:definition>No password</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Changes the users password mode.
+        </doc:para>
+        <doc:para>
+          Note that changing the password mode has the side-effect of
+          unlocking the account.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change a users password mode</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetPassword">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="password" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The crypted password.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <arg name="hint" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The password hint.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets a new password for this user.
+        </doc:para>
+        <doc:para>
+          Note that setting a password has the side-effect of
+          unlocking the account.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the password of a user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetPasswordHint">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="hint" direction="in" type="s">
+      <doc:doc>
+        <doc:summary>
+          The password hint.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the users password hint.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his own language</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the language of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <method name="SetAutomaticLogin">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="enabled" direction="in" type="b">
+      <doc:doc>
+        <doc:summary>
+          Whether to enable automatic login for this user.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Enables or disables automatic login for a user.
+        </doc:para>
+        <doc:para>
+          Note that usually only one user can have automatic login
+          enabled, so turning it on for a user will disable it for
+          the previously configured autologin user.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.set-login-option</doc:term>
+            <doc:definition>To change the login screen configuration</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
+  <property name="Uid" type="t" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The uid of the user.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="UserName" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The username of the user.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="RealName" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users real name.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="AccountType" type="i" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users account type, encoded as an integer:
+          <doc:list>
+            <doc:item>
+              <doc:term>0</doc:term>
+              <doc:definition>Standard user</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>1</doc:term>
+              <doc:definition>Administrator</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="HomeDirectory" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users home directory.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="Shell" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users shell.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="Email" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The email address.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="Language" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users language, as a locale specification like "de_DE.UTF-8".
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="XSession" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users x session.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="Location" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The users location.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="LoginFrequency" type="t" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          How often the user has logged in.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="LoginTime" type="x" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The last login time.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="LoginHistory" type="a(xxa{sv})" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The login history for this user.
+          Each entry in the array represents a login session. The first two
+          members are the login time and logout time, as timestamps (seconds since the epoch). If the 
session is still running, the logout time
+          is 0.
+        </doc:para>
+        <doc:para>
+          The a{sv} member is a dictionary containing additional information
+          about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc).
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="IconFile" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           The filename of a png file containing the users icon.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="Locked" type="b" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           Whether the users account is locked.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="PasswordMode" type="i" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          The password mode for the user account, encoded as an integer:
+          <doc:list>
+            <doc:item>
+              <doc:term>0</doc:term>
+              <doc:definition>Regular password</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>1</doc:term>
+              <doc:definition>Password must be set at next login</doc:definition>
+            </doc:item>
+            <doc:item>
+              <doc:term>2</doc:term>
+              <doc:definition>No password</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="PasswordHint" type="s" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           The password hint for the user.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="AutomaticLogin" type="b" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           Whether automatic login is enabled for the user.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="SystemAccount" type="b" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           Whether this is a 'system' account, like 'root' or 'nobody'.
+           System accounts should normally not appear in lists of
+           users, and ListCachedUsers will not include such accounts.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <property name="LocalAccount" type="b" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Whether the user is a local account or not.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
+  <signal name="Changed">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           Emitted when the user is changed.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </signal>
+
+  </interface>
+</node>
diff --git a/data/org.freedesktop.Accounts.xml b/data/org.freedesktop.Accounts.xml
new file mode 100644
index 0000000..692540a
--- /dev/null
+++ b/data/org.freedesktop.Accounts.xml
@@ -0,0 +1,228 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"; >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
+  <interface name="org.freedesktop.Accounts">
+
+    <!-- ************************************************************ -->
+
+    <method name="ListCachedUsers">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="users" direction="out" type="ao">
+        <doc:doc><doc:summary>Object paths of cached users</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Lists users which have logged into the system locally before.
+            This is not meant to return an exhaustive list of all users.
+            It is possible for <doc:ref type="method" to="Accounts.FindUserByName">FindUserByName()</doc:ref>
+            to return a user that's not on the list.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </method>
+
+    <method name="FindUserById">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="id" direction="in" type="x">
+        <doc:doc><doc:summary>The uid to look up</doc:summary></doc:doc>
+      </arg>
+      <arg name="user" direction="out" type="o">
+        <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Finds a user by uid.
+          </doc:para>
+        </doc:description>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given uid 
exists</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <method name="FindUserByName">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="name" direction="in" type="s">
+        <doc:doc><doc:summary>The username to look up</doc:summary></doc:doc>
+      </arg>
+      <arg name="user" direction="out" type="o">
+        <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Finds a user by its username.
+          </doc:para>
+        </doc:description>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given username 
exists</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <method name="CreateUser">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="name" direction="in" type="s">
+        <doc:doc><doc:summary>The username for the new user</doc:summary></doc:doc>
+      </arg>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="fullname" direction="in" type="s">
+        <doc:doc><doc:summary>The real name for the new user</doc:summary></doc:doc>
+      </arg>
+      <arg name="user" direction="out" type="o">
+        <doc:doc><doc:summary>Object path of the new user</doc:summary></doc:doc>
+      </arg>
+      <arg name="accountType" direction="in" type="i">
+        <doc:doc>
+          <doc:summary>The account type, encoded as an integer</doc:summary>
+        </doc:doc>
+      </arg>
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Creates a new user account.
+          </doc:para>
+          <doc:para>
+          The accountType argument can take the following values:
+          </doc:para>
+            <doc:list>
+              <doc:item>
+                <doc:term>0</doc:term>
+                <doc:definition>Standard user</doc:definition>
+              </doc:item>
+              <doc:item>
+                <doc:term>1</doc:term>
+                <doc:definition>Administrator</doc:definition>
+              </doc:item>
+            </doc:list>
+        </doc:description>
+        <doc:permission>
+          The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+          <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <method name="CacheUser">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="name" direction="in" type="s">
+        <doc:doc><doc:summary>The username for the user</doc:summary></doc:doc>
+      </arg>
+      <arg name="user" direction="out" type="o">
+        <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Caches a user account, so that it shows up in ListCachedUsers() output.
+            The user name may be a remote user, but the system must be able to lookup
+            the user name and resolve the user information.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+          <doc:error name="org.freedesktop.Accounts.Error.UserDoesNotExist">if the user name cannot be 
resolved</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <method name="UncacheUser">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="name" direction="in" type="s">
+        <doc:doc><doc:summary>The username for the user</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Releases all metadata about a user account, including icon, language and session. If the user 
account is
+            from a remote server and the user has never logged in before, then that account will no longer 
show up
+            in ListCachedUsers() output.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+          <doc:error name="org.freedesktop.Accounts.Error.UserDoesNotExist">if the user name cannot be 
resolved</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <method name="DeleteUser">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="id" direction="in" type="x">
+        <doc:doc><doc:summary>The uid to delete</doc:summary></doc:doc>
+      </arg>
+      <arg name="removeFiles" direction="in" type="b">
+        <doc:doc><doc:summary>Whether to remove the users files</doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Deletes a user account.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the 
appropriate PolicyKit authorization</doc:error>
+          <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <signal name="UserAdded">
+      <arg name="user" type="o">
+        <doc:doc><doc:summary>Object path of the user that was added.</doc:summary></doc:doc>
+      </arg>
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Emitted when a user is added.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+
+    <signal name="UserDeleted">
+      <arg name="user" type="o">
+        <doc:doc><doc:summary>Object path of the user that was deleted.</doc:summary></doc:doc>
+      </arg>
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Emitted when a user is deleted.
+          </doc:para>
+        </doc:description>
+      </doc:doc>
+    </signal>
+
+   <property name="DaemonVersion" type="s" access="read">
+     <doc:doc>
+       <doc:description>
+         <doc:para>
+           The version of the running daemon.
+         </doc:para>
+       </doc:description>
+     </doc:doc>
+   </property>
+
+  </interface>
+</node>
diff --git a/src/account.c b/src/account.c
new file mode 100644
index 0000000..372bdf1
--- /dev/null
+++ b/src/account.c
@@ -0,0 +1,260 @@
+#define _GNU_SOURCE 1
+
+#include "config.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gtk/gtk.h>
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gio/gunixfdlist.h>
+
+#include "xdg-desktop-portal-dbus.h"
+#include "shell-dbus.h"
+
+#include "account.h"
+#include "accountdialog.h"
+#include "request.h"
+#include "utils.h"
+#include "externalwindow.h"
+
+static OrgFreedesktopAccountsUser *user;
+
+typedef struct {
+  XdpImplAccount *impl;
+  GDBusMethodInvocation *invocation;
+  Request *request;
+
+  GtkWidget *dialog;
+  ExternalWindow *external_parent;
+
+  int response;
+  char *user_name;
+  char *real_name;
+  char *icon_uri;
+} AccountDialogHandle;
+
+static void
+account_dialog_handle_free (gpointer data)
+{
+  AccountDialogHandle *handle = data;
+
+  g_clear_object (&handle->external_parent);
+  g_object_unref (handle->request);
+  g_object_unref (handle->dialog);
+  g_free (handle->user_name);
+  g_free (handle->real_name);
+  g_free (handle->icon_uri);
+
+  g_free (handle);
+}
+
+static void
+account_dialog_handle_close (AccountDialogHandle *handle)
+{
+  gtk_window_destroy (GTK_WINDOW (handle->dialog));
+  account_dialog_handle_free (handle);
+}
+
+static void
+send_response (AccountDialogHandle *handle)
+{
+  GVariantBuilder opt_builder;
+
+  g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&opt_builder, "{sv}", "id", g_variant_new_string (handle->user_name ? 
handle->user_name : ""));
+  g_variant_builder_add (&opt_builder, "{sv}", "name", g_variant_new_string (handle->real_name ? 
handle->real_name : ""));
+  g_variant_builder_add (&opt_builder, "{sv}", "image", g_variant_new_string (handle->icon_uri ? 
handle->icon_uri : ""));
+
+  if (handle->request->exported)
+    request_unexport (handle->request);
+
+  xdp_impl_account_complete_get_user_information (handle->impl,
+                                                  handle->invocation,
+                                                  handle->response,
+                                                  g_variant_builder_end (&opt_builder));
+
+  account_dialog_handle_close (handle);
+}
+
+static void
+account_dialog_done (GtkWidget  *widget,
+                     int         response,
+                     const char *user_name,
+                     const char *real_name,
+                     const char *icon_file,
+                     gpointer    user_data)
+{
+  AccountDialogHandle *handle = user_data;
+
+  g_clear_pointer (&handle->user_name, g_free);
+  g_clear_pointer (&handle->real_name, g_free);
+  g_clear_pointer (&handle->icon_uri, g_free);
+
+  switch (response)
+    {
+    default:
+      g_warning ("Unexpected response: %d", response);
+      /* Fall through */
+    case GTK_RESPONSE_DELETE_EVENT:
+      handle->response = 2;
+      break;
+
+    case GTK_RESPONSE_CANCEL:
+      handle->response = 1;
+      break;
+
+    case GTK_RESPONSE_OK:
+      handle->user_name = g_strdup (user_name);
+      handle->real_name = g_strdup (real_name);
+      if (icon_file)
+        handle->icon_uri = g_filename_to_uri (icon_file, NULL, NULL);
+      handle->response = 0;
+      break;
+    }
+
+  send_response (handle);
+}
+
+static gboolean
+handle_close (XdpImplRequest *object,
+              GDBusMethodInvocation *invocation,
+              AccountDialogHandle *handle)
+{
+  GVariantBuilder opt_builder;
+
+  g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+  xdp_impl_account_complete_get_user_information (handle->impl,
+                                                  handle->invocation,
+                                                  2,
+                                                  g_variant_builder_end (&opt_builder));
+  account_dialog_handle_close (handle);
+
+  return FALSE;
+}
+
+static gboolean
+handle_get_user_information (XdpImplAccount        *object,
+                             GDBusMethodInvocation *invocation,
+                             const char            *arg_handle,
+                             const char            *arg_app_id,
+                             const char            *arg_parent_window,
+                             GVariant              *arg_options)
+{
+  g_autoptr(Request) request = NULL;
+  const char *sender;
+  AccountDialogHandle *handle;
+  const char *user_name;
+  const char *real_name;
+  const char *icon_file;
+  GtkWidget *dialog;
+  GdkSurface *surface;
+  GdkDisplay *display;
+  ExternalWindow *external_parent = NULL;
+  GtkWidget *fake_parent;
+  const char *reason;
+
+  sender = g_dbus_method_invocation_get_sender (invocation);
+
+  request = request_new (sender, arg_app_id, arg_handle);
+
+  user_name = org_freedesktop_accounts_user_get_user_name (user);
+  real_name = org_freedesktop_accounts_user_get_real_name (user);
+  icon_file = org_freedesktop_accounts_user_get_icon_file (user);
+
+  if (!g_variant_lookup (arg_options, "reason", "&s", &reason))
+    reason = NULL;
+
+  if (arg_parent_window)
+    {
+      external_parent = create_external_window_from_handle (arg_parent_window);
+      if (!external_parent)
+        g_warning ("Failed to associate portal window with parent window %s",
+                   arg_parent_window);
+    }
+
+  if (external_parent)
+    display = external_window_get_display (external_parent);
+  else
+    display = gdk_display_get_default ();
+
+  fake_parent = g_object_new (GTK_TYPE_WINDOW,
+                              "display", display,
+                              NULL);
+  g_object_ref_sink (fake_parent);
+
+  dialog = GTK_WIDGET (account_dialog_new (arg_app_id, user_name, real_name, icon_file, reason));
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent));
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+  handle = g_new0 (AccountDialogHandle, 1);
+  handle->impl = object;
+  handle->invocation = invocation;
+  handle->request = g_object_ref (request);
+  handle->dialog = g_object_ref (dialog);
+  handle->external_parent = external_parent;
+  handle->user_name = g_strdup (user_name);
+  handle->real_name = g_strdup (real_name);
+  handle->icon_uri = g_filename_to_uri (icon_file, NULL, NULL);
+
+  g_signal_connect (request, "handle-close", G_CALLBACK (handle_close), handle);
+
+  g_signal_connect (dialog, "done", G_CALLBACK (account_dialog_done), handle);
+
+  gtk_widget_realize (dialog);
+
+  surface = gtk_native_get_surface (GTK_NATIVE (dialog));
+  if (external_parent)
+    external_window_set_parent_of (external_parent, surface);
+
+  gtk_widget_show (dialog);
+
+  request_export (request, g_dbus_method_invocation_get_connection (invocation));
+
+  return TRUE;
+}
+
+static GDBusConnection *system_bus;
+
+gboolean
+account_init (GDBusConnection  *bus,
+              GError          **error)
+{
+  GDBusInterfaceSkeleton *helper;
+  g_autofree char *object_path = NULL;
+
+  system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+  if (system_bus == NULL)
+    return FALSE;
+
+  helper = G_DBUS_INTERFACE_SKELETON (xdp_impl_account_skeleton_new ());
+
+  g_signal_connect (helper, "handle-get-user-information", G_CALLBACK (handle_get_user_information), NULL);
+
+  if (!g_dbus_interface_skeleton_export (helper,
+                                         bus,
+                                         DESKTOP_PORTAL_OBJECT_PATH,
+                                         error))
+    return FALSE;
+
+  object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", getuid ());
+
+  user = org_freedesktop_accounts_user_proxy_new_sync (system_bus,
+                                                       G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+                                                       "org.freedesktop.Accounts",
+                                                       object_path,
+                                                       NULL,
+                                                       error);
+  if (user == NULL)
+    return FALSE;
+
+  g_debug ("providing %s", g_dbus_interface_skeleton_get_info (helper)->name);
+
+  return TRUE;
+}
diff --git a/src/account.h b/src/account.h
new file mode 100644
index 0000000..01f353d
--- /dev/null
+++ b/src/account.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program 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/>.
+ *
+ * Authors:
+ *       Matthias Clasen <mclasen redhat com>
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+gboolean account_init (GDBusConnection  *bus,
+                       GError          **error);
diff --git a/src/accountdialog.c b/src/accountdialog.c
new file mode 100644
index 0000000..6f5577e
--- /dev/null
+++ b/src/accountdialog.c
@@ -0,0 +1,252 @@
+#define _GNU_SOURCE 1
+
+#include <string.h>
+
+#include <adwaita.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <glib/gi18n.h>
+
+#include "accountdialog.h"
+
+struct _AccountDialog {
+  GtkWindow parent;
+
+  GtkWidget *heading;
+  GtkWidget *accept_button;
+  GtkWidget *name;
+  GtkWidget *fullname;
+  AdwAvatar *image;
+
+  char *icon_file;
+};
+
+struct _AccountDialogClass {
+  GtkWindowClass parent_class;
+};
+
+enum {
+  DONE,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (AccountDialog, account_dialog, GTK_TYPE_WINDOW)
+
+static void
+account_dialog_init (AccountDialog *dialog)
+{
+  gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+account_dialog_finalize (GObject *object)
+{
+  AccountDialog *dialog = ACCOUNT_DIALOG (object);
+
+  g_clear_pointer (&dialog->icon_file, g_free);
+
+  G_OBJECT_CLASS (account_dialog_parent_class)->finalize (object);
+}
+
+static gboolean
+account_dialog_close_request (GtkWindow *dialog)
+{
+  gtk_widget_hide (GTK_WIDGET (dialog));
+
+  g_signal_emit (dialog, signals[DONE], 0, GTK_RESPONSE_CANCEL, NULL);
+
+  return TRUE;
+}
+
+static void
+button_clicked (GtkWidget     *button,
+                AccountDialog *dialog)
+{
+  int response;
+  const char *user_name;
+  const char *real_name;
+
+  gtk_widget_hide (GTK_WIDGET (dialog));
+
+  if (button == dialog->accept_button)
+    response = GTK_RESPONSE_OK;
+  else
+    response = GTK_RESPONSE_CANCEL;
+
+  user_name = gtk_editable_get_text (GTK_EDITABLE (dialog->name));
+  real_name = gtk_editable_get_text (GTK_EDITABLE (dialog->fullname));
+  g_signal_emit (dialog, signals[DONE], 0, response, user_name, real_name, dialog->icon_file);
+}
+
+static void
+dialog_set_icon_file (AccountDialog *dialog,
+                      const char    *icon_file)
+{
+  g_clear_pointer (&dialog->icon_file, g_free);
+  dialog->icon_file = g_strdup (icon_file);
+
+  if (icon_file)
+    {
+      g_autoptr(GdkTexture) texture = NULL;
+      g_autoptr(GError) error = NULL;
+      g_autoptr(GFile) file = NULL;
+
+      file = g_file_new_for_path (icon_file);
+      texture = gdk_texture_new_from_file (file, &error);
+      if (error)
+        g_warning ("Failed to load account %s: %s", icon_file, error->message);
+      adw_avatar_set_custom_image (dialog->image, GDK_PAINTABLE (texture));
+      gtk_widget_remove_css_class (GTK_WIDGET (dialog->image), "dim-label");
+    }
+  else
+    {
+      adw_avatar_set_custom_image (dialog->image, NULL);
+      adw_avatar_set_icon_name (dialog->image, "camera-photo-symbolic");
+      gtk_widget_add_css_class (GTK_WIDGET (dialog->image), "dim-label");
+    }
+}
+
+static void
+file_chooser_response (GtkWidget *widget,
+                       int        response,
+                       gpointer   user_data)
+{
+  AccountDialog *dialog = user_data;
+  g_autoptr (GFile) file = NULL;
+  g_autofree char *path = NULL;
+
+  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
+  path = file ? g_file_get_path (file) : NULL;
+
+  switch (response)
+    {
+    default:
+      g_warning ("Unexpected response: %d", response);
+      /* Fall through */
+    case GTK_RESPONSE_DELETE_EVENT:
+    case GTK_RESPONSE_CANCEL:
+      break;
+
+    case GTK_RESPONSE_OK:
+      dialog_set_icon_file (dialog, path);
+      break;
+
+    case GTK_RESPONSE_CLOSE:
+      dialog_set_icon_file (dialog, NULL);
+      break;
+    }
+
+  gtk_window_destroy (GTK_WINDOW (widget));
+}
+
+static void
+image_button_clicked (AccountDialog *dialog)
+{
+  GtkWidget *chooser;
+  GtkFileFilter *filter;
+
+  chooser = gtk_file_chooser_dialog_new (_("Select an Image"),
+                                         GTK_WINDOW (dialog),
+                                         GTK_FILE_CHOOSER_ACTION_OPEN,
+                                         _("Cancel"), GTK_RESPONSE_CANCEL,
+                                         _("Select"), GTK_RESPONSE_OK,
+                                         _("Clear"),  GTK_RESPONSE_CLOSE,
+                                         NULL);
+  gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Images"));
+  gtk_file_filter_add_pixbuf_formats (filter);
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
+
+  g_signal_connect (chooser, "response", G_CALLBACK (file_chooser_response), dialog);
+
+  gtk_widget_show (chooser);
+}
+
+static void
+account_dialog_class_init (AccountDialogClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GtkWindowClass *window_class = GTK_WINDOW_CLASS (class);
+
+  object_class->finalize = account_dialog_finalize;
+
+  window_class->close_request = account_dialog_close_request;
+
+  signals[DONE] = g_signal_new ("done",
+                                G_TYPE_FROM_CLASS (class),
+                                G_SIGNAL_RUN_LAST,
+                                0,
+                                NULL, NULL,
+                                NULL,
+                                G_TYPE_NONE, 4,
+                                G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/freedesktop/portal/desktop/gnome/accountdialog.ui");
+  gtk_widget_class_bind_template_child (widget_class, AccountDialog, accept_button);
+  gtk_widget_class_bind_template_child (widget_class, AccountDialog, heading);
+  gtk_widget_class_bind_template_child (widget_class, AccountDialog, name);
+  gtk_widget_class_bind_template_child (widget_class, AccountDialog, fullname);
+  gtk_widget_class_bind_template_child (widget_class, AccountDialog, image);
+
+  gtk_widget_class_bind_template_callback (widget_class, button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, image_button_clicked);
+}
+
+AccountDialog *
+account_dialog_new (const char *app_id,
+                    const char *user_name,
+                    const char *real_name,
+                    const char *icon_file,
+                    const char *reason)
+{
+  AccountDialog *dialog;
+  g_autofree char *heading = NULL;
+  g_autoptr(GdkTexture) texture = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) file = NULL;
+
+  dialog = g_object_new (account_dialog_get_type (), NULL);
+
+  if (strcmp (app_id, "") != 0)
+    {
+      g_autofree char *id = NULL;
+      g_autoptr(GAppInfo) info = NULL;
+      id = g_strconcat (app_id, ".desktop", NULL);
+      info = G_APP_INFO (g_desktop_app_info_new (id));
+      heading = g_strdup_printf (_("Share your personal information with %1$s? %2$s"),
+                                 g_app_info_get_display_name (info),
+                                 reason ?: "");
+    }
+  else
+    {
+      heading = g_strdup_printf (_("Share your personal information with the requesting application? %s"),
+                                 reason ?: "");
+    }
+
+  gtk_label_set_label (GTK_LABEL (dialog->heading), heading);
+  gtk_editable_set_text (GTK_EDITABLE (dialog->name), user_name);
+  gtk_editable_set_text (GTK_EDITABLE (dialog->fullname), real_name);
+
+  file = g_file_new_for_path (icon_file);
+  texture = gdk_texture_new_from_file (file, &error);
+  if (error)
+    {
+      g_warning ("Failed to load account %s: %s", icon_file, error->message);
+    }
+  else
+    {
+      adw_avatar_set_custom_image (dialog->image, GDK_PAINTABLE (texture));
+    }
+
+  dialog->icon_file = g_strdup (icon_file);
+
+  return dialog;
+}
diff --git a/src/accountdialog.h b/src/accountdialog.h
new file mode 100644
index 0000000..e209343
--- /dev/null
+++ b/src/accountdialog.h
@@ -0,0 +1,17 @@
+#include <gtk/gtk.h>
+
+#define ACCOUNT_TYPE_DIALOG (account_dialog_get_type ())
+#define ACCOUNT_DIALOG(object) (G_TYPE_CHECK_INSTANCE_CAST (object, ACCOUNT_TYPE_DIALOG, AccountDialog))
+
+typedef struct _AccountDialog AccountDialog;
+typedef struct _AccountDialogClass AccountDialogClass;
+
+AccountDialog * account_dialog_new (const char *app_id,
+                                    const char *user_name,
+                                    const char *real_name,
+                                    const char *icon_file,
+                                    const char *reason);
+
+const char *account_dialog_get_user_name (AccountDialog *dialog);
+const char *account_dialog_get_real_name (AccountDialog *dialog);
+const char *account_dialog_get_icon_file (AccountDialog *dialog);
diff --git a/src/accountdialog.ui b/src/accountdialog.ui
new file mode 100644
index 0000000..581e9c3
--- /dev/null
+++ b/src/accountdialog.ui
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="xdg-desktop-portal-gnome">
+  <template class="AccountDialog" parent="GtkWindow">
+    <property name="title" translatable="yes">Share Details</property>
+    <property name="resizable">0</property>
+
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="titlebar">
+        <property name="show-title-buttons">False</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">_Cancel</property>
+            <property name="use-underline">1</property>
+            <signal name="clicked" handler="button_clicked"/>
+          </object>
+        </child>
+        <child type="end">
+          <object class="GtkButton" id="accept_button">
+            <property name="label" translatable="yes">_Share</property>
+            <property name="use-underline">1</property>
+            <signal name="clicked" handler="button_clicked"/>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="margin-top">24</property>
+        <property name="margin-bottom">24</property>
+        <property name="margin-start">36</property>
+        <property name="margin-end">36</property>
+        <property name="spacing">24</property>
+
+        <child>
+          <object class="GtkLabel" id="heading">
+            <property name="wrap">1</property>
+            <property name="xalign">0.5</property>
+            <property name="max-width-chars">32</property>
+            <property name="width-chars">32</property>
+          </object>
+        </child>
+
+        <!-- Profile Picture -->
+        <child>
+          <object class="GtkButton" id="button">
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <signal name="clicked" handler="image_button_clicked" swapped="yes"/>
+            <child>
+              <object class="AdwAvatar" id="image">
+                <property name="size">96</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Rows -->
+        <child>
+          <object class="GtkListBox">
+            <property name="selection-mode">none</property>
+            <style>
+              <class name="content" />
+            </style>
+
+            <child>
+              <object class="AdwActionRow">
+                <property name="title" translatable="yes">Name</property>
+                <property name="activatable-widget">fullname</property>
+                <child>
+                  <object class="GtkEntry" id="fullname">
+                    <property name="valign">center</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+            <child>
+              <object class="AdwActionRow">
+                <property name="title" translatable="yes">Username</property>
+                <property name="activatable-widget">name</property>
+                <child>
+                  <object class="GtkEntry" id="name">
+                    <property name="valign">center</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+
+          </object>
+        </child>
+
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/meson.build b/src/meson.build
index f26185c..e38787f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -6,6 +6,7 @@ xdg_desktop_portal_dep = dependency('xdg-desktop-portal', version: '>= 1.5')
 # Desktop Portal D-Bus interfaces
 desktop_portal_interfaces_dir = xdg_desktop_portal_dep.get_pkgconfig_variable('interfaces_dir')
 desktop_portal_dbus_interfaces = [
+  desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Account.xml',
   desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Print.xml',
   desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Request.xml',
   desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Session.xml',
@@ -30,6 +31,8 @@ built_sources = gnome.gdbus_codegen(
 # Mutter / GNOME Shell D-Bus interfaces
 top_srcdir = meson.source_root()
 shell_dbus_interfaces = files(
+  top_srcdir / 'data' / 'org.freedesktop.Accounts.User.xml',
+  top_srcdir / 'data' / 'org.freedesktop.Accounts.xml',
        top_srcdir / 'data' / 'org.gnome.Mutter.DisplayConfig.xml',
        top_srcdir / 'data' / 'org.gnome.Mutter.RemoteDesktop.xml',
        top_srcdir / 'data' / 'org.gnome.Mutter.ScreenCast.xml',
@@ -78,6 +81,8 @@ deps = [
 ]
 
 sources = built_sources + files(
+  'account.c',
+  'accountdialog.c',
   'appchooser.c',
   'appchooserrow.c',
   'appchooserdialog.c',
diff --git a/src/xdg-desktop-portal-gnome.c b/src/xdg-desktop-portal-gnome.c
index e896195..6370b7c 100644
--- a/src/xdg-desktop-portal-gnome.c
+++ b/src/xdg-desktop-portal-gnome.c
@@ -42,6 +42,7 @@
 
 #include "xdg-desktop-portal-dbus.h"
 
+#include "account.h"
 #include "appchooser.h"
 #include "background.h"
 #include "filechooser.h"
@@ -110,6 +111,12 @@ on_bus_acquired (GDBusConnection *connection,
       g_clear_error (&error);
     }
 
+  if (!account_init (connection, &error))
+    {
+      g_warning ("error: %s\n", error->message);
+      g_clear_error (&error);
+    }
+
   if (!app_chooser_init (connection, &error))
     {
       g_warning ("error: %s\n", error->message);
diff --git a/src/xdg-desktop-portal-gnome.gresource.xml b/src/xdg-desktop-portal-gnome.gresource.xml
index 12cc0b0..aee9b53 100644
--- a/src/xdg-desktop-portal-gnome.gresource.xml
+++ b/src/xdg-desktop-portal-gnome.gresource.xml
@@ -1,6 +1,7 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <gresources>
   <gresource prefix='/org/freedesktop/portal/desktop/gnome'>
+    <file>accountdialog.ui</file>
     <file>appchooserdialog.ui</file>
     <file>appchooserrow.ui</file>
     <file>screenshotdialog.ui</file>


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