diff --git a/data/applications.gresource.xml b/data/applications.gresource.xml new file mode 100644 index 00000000..067e48d6 --- /dev/null +++ b/data/applications.gresource.xml @@ -0,0 +1,6 @@ + + + + background.svg + + diff --git a/data/background.svg b/data/background.svg new file mode 100644 index 00000000..3c2b63b9 --- /dev/null +++ b/data/background.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/data/meson.build b/data/meson.build index be9909aa..d2c22809 100644 --- a/data/meson.build +++ b/data/meson.build @@ -1,3 +1,9 @@ +gresource = gnome.compile_resources( + 'applications-resources', + 'applications.gresource.xml', + source_dir: meson.current_source_dir() +) + i18n.merge_file( input: 'applications.metainfo.xml.in', output: 'io.elementary.settings.applications.metainfo.xml', diff --git a/src/Permissions/Backend/PermissionStore.vala b/src/Permissions/Backend/PermissionStore.vala new file mode 100644 index 00000000..700ddcf6 --- /dev/null +++ b/src/Permissions/Backend/PermissionStore.vala @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: LGPL-3.0-or-later + * SPDX-FileCopyrightText: 2023 elementary, Inc. (https://elementary.io) + */ + +[DBus (name = "org.freedesktop.impl.portal.PermissionStore", timeout = 120000)] +public interface PermissionStoreDBus : GLib.Object { + public signal void changed (string table, string id, bool deleted, GLib.Variant data, [DBus (signature = "a{sas}")] GLib.Variant permissions); + public abstract async void set_permission (string table, bool create, string id, string app, string[] permissions) throws DBusError, IOError; + public abstract async string[] get_permission (string table, string id, string app) throws DBusError, IOError; +} + +public class Permissions.PermissionStore : GLib.Object { + public signal void changed (); + + private const string DBUS_NAME = "org.freedesktop.impl.portal.PermissionStore"; + private const string DBUS_PATH = "/org/freedesktop/impl/portal/PermissionStore"; + private const uint RECONNECT_TIMEOUT = 5000U; + + private static PermissionStore? instance; + public static unowned PermissionStore get_default () { + if (instance == null) { + instance = new PermissionStore (); + } + + return instance; + } + + public PermissionStoreDBus dbus { get; private set; default = null; } + + construct { + Bus.watch_name ( + BusType.SESSION, DBUS_NAME, AUTO_START, + () => try_connect (), name_vanished_callback + ); + } + + private PermissionStore () { } + + private void try_connect () { + Bus.get_proxy.begin (SESSION, DBUS_NAME, DBUS_PATH, 0, null, (obj, res) => { + try { + dbus = Bus.get_proxy.end (res); + dbus.changed.connect (() => changed ()); + } catch (Error e) { + critical (e.message); + Timeout.add (RECONNECT_TIMEOUT, () => { + try_connect (); + return GLib.Source.REMOVE; + }); + } + }); + } + + private void name_vanished_callback (DBusConnection connection, string name) { + dbus = null; + } + + public void set_permission (string table, string id, string app, string[] permissions) { + dbus.set_permission.begin (table, false, id, app, permissions, (obj, res) => { + try { + dbus.set_permission.end (res); + } catch (Error e) { + critical (e.message); + var dialog = new Granite.MessageDialog ( + _("Couldn't set permission"), + e.message, + new ThemedIcon ("preferences-system") + ) { + badge_icon = new ThemedIcon ("dialog-error"), + modal = true, + transient_for = ((Gtk.Application) GLib.Application.get_default ()).active_window + }; + dialog.present (); + dialog.response.connect (dialog.destroy); + } + }); + } + + public async string[] get_permission (string table, string id, string app) { + try { + return yield dbus.get_permission (table, id, app); + } catch (Error e) { + var dialog = new Granite.MessageDialog ( + _("Couldn't get permission"), + e.message, + new ThemedIcon ("preferences-system") + ) { + badge_icon = new ThemedIcon ("dialog-error"), + modal = true, + transient_for = ((Gtk.Application) GLib.Application.get_default ()).active_window + }; + dialog.present (); + dialog.response.connect (dialog.destroy); + } + + return { null }; + } +} diff --git a/src/Permissions/Widgets/AppSettingsView.vala b/src/Permissions/Widgets/AppSettingsView.vala index 9b27c4b2..f72b2a54 100644 --- a/src/Permissions/Widgets/AppSettingsView.vala +++ b/src/Permissions/Widgets/AppSettingsView.vala @@ -22,21 +22,36 @@ public class Permissions.Widgets.AppSettingsView : Switchboard.SettingsPage { public Backend.App? selected_app { get; set; default = null; } - private Gtk.ListBox list_box; + private const string BACKGROUND_TABLE = "background"; + private const string BACKGROUND_ID = "background"; + + private Gtk.ListBox sandbox_box; + private Gtk.ListBox permission_box; private Gtk.Button reset_button; + private PermissionSettingsWidget background_row; construct { notify["selected-app"].connect (update_view); - list_box = new Gtk.ListBox () { + permission_box = new Gtk.ListBox () { + hexpand = true, + selection_mode = NONE + }; + permission_box.add_css_class ("boxed-list"); + permission_box.add_css_class (Granite.STYLE_CLASS_RICH_LIST); + + sandbox_box = new Gtk.ListBox () { hexpand = true, - valign = START, selection_mode = NONE }; - list_box.add_css_class ("boxed-list"); - list_box.add_css_class (Granite.STYLE_CLASS_RICH_LIST); + sandbox_box.add_css_class ("boxed-list"); + sandbox_box.add_css_class (Granite.STYLE_CLASS_RICH_LIST); + + var box = new Gtk.Box (VERTICAL, 24); + box.append (permission_box); + box.append (sandbox_box); - child = list_box; + child = box; reset_button = add_button (_("Reset to Defaults")); @@ -51,10 +66,11 @@ public class Permissions.Widgets.AppSettingsView : Switchboard.SettingsPage { } private void update_view () { - list_box.remove_all (); + permission_box.remove_all (); + sandbox_box.remove_all (); if (selected_app == null) { - reset_button.sensitive = false; + sensitive = false; return; } @@ -113,9 +129,27 @@ public class Permissions.Widgets.AppSettingsView : Switchboard.SettingsPage { should_enable_reset = true; } - list_box.append (override_row); + sandbox_box.append (override_row); }); + var permission_store = PermissionStore.get_default (); + + background_row = new PermissionSettingsWidget ( + _("Background Activity"), + _("Perform tasks and use system resources while its window is closed."), + "permissions-background" + ); + + background_row.notify["active"].connect (() => { + string[] permissions = { background_row.active ? "yes" : "no" }; + permission_store.set_permission (BACKGROUND_TABLE, BACKGROUND_ID, selected_app.id, permissions); + }); + + update_permissions.begin (); + permission_store.notify["dbus"].connect (update_permissions); + permission_store.changed.connect (update_permissions); + + sensitive = true; reset_button.sensitive = should_enable_reset; update_property (Gtk.AccessibleProperty.LABEL, _("%s permissions").printf (selected_app.name), -1); @@ -123,6 +157,27 @@ public class Permissions.Widgets.AppSettingsView : Switchboard.SettingsPage { icon = selected_app.icon; } + private async void update_permissions () { + var permission_store = PermissionStore.get_default (); + if (permission_store.dbus == null) { + permission_box.sensitive = false; + return; + } + + permission_box.sensitive = true; + + var background_permission = yield permission_store.get_permission (BACKGROUND_TABLE, BACKGROUND_ID, selected_app.id); + if (background_permission[0] != null) { + background_row.active = background_permission[0] == "yes"; + + if (background_row.parent == null) { + permission_box.append (background_row); + } + } + + permission_box.visible = permission_box.get_row_at_index (0) != null; + } + private void change_permission_settings (Backend.PermissionSettings settings) { if (selected_app == null) { return; diff --git a/src/meson.build b/src/meson.build index 2a5f6e5e..5a7cedef 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,6 +16,7 @@ plug_files = files( 'Permissions/Backend/AppManager.vala', 'Permissions/Backend/FlatpakManager.vala', 'Permissions/Backend/PermissionSettings.vala', + 'Permissions/Backend/PermissionStore.vala', 'Permissions/Widgets/AppSettingsView.vala', 'Permissions/Widgets/PermissionSettingsWidget.vala', 'Permissions/Widgets/SidebarRow.vala', @@ -27,6 +28,7 @@ switchboard_plugsdir = switchboard_dep.get_pkgconfig_variable('plugsdir', define shared_module( meson.project_name(), + gresource, plug_files, conf_file, dependencies: [