Skip to content

Commit

Permalink
Use Location Portal (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
danirabbit committed May 4, 2023
1 parent a459e18 commit d868a3f
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 121 deletions.
18 changes: 18 additions & 0 deletions src/DBus/PermissionStore.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 SecurityPrivacy.PermissionStore : GLib.Object {
public signal void changed (string table, string id, bool deleted, GLib.Variant data, [DBus (signature = "a{sas}")] GLib.Variant permissions);
public abstract uint version { get; }
public abstract void lookup (string table, string id, [DBus (signature = "a{sas}")] out GLib.Variant permissions, out GLib.Variant data) throws DBusError, IOError;
public abstract void set (string table, bool create, string id, [DBus (signature = "a{sas}")] GLib.Variant app_permissions, GLib.Variant data) throws DBusError, IOError;
public abstract void delete (string table, string id) throws DBusError, IOError;
public abstract void set_value (string table, bool create, string id, GLib.Variant data) throws DBusError, IOError;
public abstract void set_permission (string table, bool create, string id, string app, string[] permissions) throws DBusError, IOError;
public abstract void delete_permission (string table, string id, string app) throws DBusError, IOError;
public abstract string[] get_permission (string table, string id, string app) throws DBusError, IOError;
public abstract string[] list (string table) throws DBusError, IOError;
}
31 changes: 9 additions & 22 deletions src/Plug.vala
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ namespace SecurityPrivacy {

ServiceList service_list;

bool location_agent_installed = false;

private const string FIREWALL = "firewall";
private const string HOUSEKEEPING = "housekeeping";
private const string HISTORY = "tracking";
Expand All @@ -52,24 +50,18 @@ namespace SecurityPrivacy {
icon: "preferences-system-privacy",
supported_settings: new Gee.TreeMap<string, string?> (null, null));

location_agent_installed = SecurityPrivacy.LocationPanel.location_agent_installed ();
supported_settings.set ("security", null);
supported_settings.set ("security/firewall", FIREWALL);
supported_settings.set ("security/locking", LOCKING);
supported_settings.set ("privacy", HISTORY);
supported_settings.set ("privacy/location", LOCATION);
supported_settings.set ("privacy/trash", HOUSEKEEPING);
supported_settings.set ("security/firewall", FIREWALL);
supported_settings.set ("security/locking", LOCKING);
supported_settings.set ("security", null);

// DEPRECATED
supported_settings.set ("security/privacy", HISTORY);
supported_settings.set ("security/housekeeping", HOUSEKEEPING);
supported_settings.set ("security/privacy", HISTORY);
supported_settings.set ("security/privacy/location", LOCATION);
supported_settings.set ("security/screensaver", LOCKING);

if (location_agent_installed) {
supported_settings.set ("privacy/location", LOCATION);

// DEPRECATED
supported_settings.set ("security/privacy/location", LOCATION);
}
}

public override Gtk.Widget get_widget () {
Expand Down Expand Up @@ -132,16 +124,13 @@ namespace SecurityPrivacy {
var locking = new LockPanel ();
firewall = new FirewallPanel ();
housekeeping = new HouseKeepingPanel ();
location = new LocationPanel ();

stack.add_titled (tracking, HISTORY, _("Privacy"));
stack.add_titled (locking, LOCKING, _("Locking"));
stack.add_titled (firewall, FIREWALL, _("Firewall"));
stack.add_titled (housekeeping, HOUSEKEEPING, _("Housekeeping"));

if (location_agent_installed) {
location = new LocationPanel ();
stack.add_titled (location, LOCATION, _("Location Services"));
}
stack.add_titled (location, LOCATION, _("Location Services"));

service_list = new ServiceList ();

Expand Down Expand Up @@ -197,9 +186,7 @@ namespace SecurityPrivacy {
_("Housekeeping"),
_("Number of days to keep trashed and temporary files")
), HOUSEKEEPING);
if (location_agent_installed) {
map.set ("%s%s".printf (display_name, _("Location Services")), LOCATION);
}
map.set ("%s%s".printf (display_name, _("Location Services")), LOCATION);
return map;
}
}
Expand Down
194 changes: 103 additions & 91 deletions src/Views/LocationPanel.vala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
/*-
* Copyright (c) 2017-2018 elementary LLC. (https://elementary.io)
* Copyright 2017-2023 elementary, Inc. (https://elementary.io)
* Copyright (C) 2017 David Hewitt <davidmhewitt@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
Expand All @@ -21,21 +20,12 @@
*/

public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
private const string LOCATION_AGENT_ID = "io.elementary.desktop.agent-geoclue2";
private const string PERMISSIONS_TABLE = "location";
private const string PERMISSIONS_ID = "location";

private GLib.Settings location_settings;
private Variant remembered_apps;
private VariantDict remembered_apps_dict;
private Gtk.ListBox listbox;
private Gtk.Stack disabled_stack;

private enum Columns {
AUTHORIZED,
NAME,
ICON,
APP_ID,
N_COLUMNS
}
private ListStore liststore;
private PermissionStore permission_store;

public LocationPanel () {
Object (
Expand All @@ -47,7 +37,7 @@ public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
}

construct {
location_settings = new GLib.Settings (LOCATION_AGENT_ID);
liststore = new ListStore (typeof (AppPermission));

var placeholder = new Granite.Widgets.AlertView (
_("No Apps Are Using Location Services"),
Expand All @@ -56,16 +46,18 @@ public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
);
placeholder.show_all ();

listbox = new Gtk.ListBox ();
listbox.activate_on_single_click = true;
var listbox = new Gtk.ListBox () {
activate_on_single_click = true
};
listbox.bind_model (liststore, create_widget_func);
listbox.set_placeholder (placeholder);

populate_app_listbox ();

var scrolled = new Gtk.ScrolledWindow (null, null);
scrolled.expand = true;
scrolled.visible = true;
scrolled.add (listbox);
var scrolled = new Gtk.ScrolledWindow (null, null) {
child = listbox,
hexpand = true,
vexpand = true,
visible = true
};

var alert = new Granite.Widgets.AlertView (
_("Location Services Are Disabled"),
Expand All @@ -82,12 +74,14 @@ public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
disabled_stack.add_named (alert, "disabled");
disabled_stack.add_named (scrolled, "enabled");

var frame = new Gtk.Frame (null);
frame.add (disabled_stack);
var frame = new Gtk.Frame (null) {
child = disabled_stack
};

content_area.add (frame);

location_settings.bind ("location-enabled", status_switch, "active", SettingsBindFlags.DEFAULT);
var location_settings = new Settings ("org.gnome.system.location");
location_settings.bind ("enabled", status_switch, "active", SettingsBindFlags.DEFAULT);

update_status ();

Expand All @@ -99,11 +93,28 @@ public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
((LocationRow) row).on_active_changed ();
});

location_settings.changed.connect ((key) => {
populate_app_listbox ();
init_interfaces.begin ((obj, res) => {
init_interfaces.end (res);
load_permissions ();
});
}

private async void init_interfaces () {
try {
permission_store = yield Bus.get_proxy (BusType.SESSION, "org.freedesktop.impl.portal.PermissionStore", "/org/freedesktop/impl/portal/PermissionStore");

permission_store.changed.connect ((table, id, deleted, data, permissions) => {
if (table != PERMISSIONS_TABLE || id != PERMISSIONS_ID) {
return;
}

load_permissions ();
});
} catch (IOError e) {
critical ("Unable to connect to GNOME session interface: %s", e.message);
}
}

private void update_status () {
if (status_switch.active) {
disabled_stack.visible_child_name = "enabled";
Expand All @@ -118,91 +129,92 @@ public class SecurityPrivacy.LocationPanel : Granite.SimpleSettingsPage {
}
}

private void populate_app_listbox () {
load_remembered_apps ();

foreach (var row in listbox.get_children ()) {
listbox.remove (row);
}

foreach (var app in remembered_apps) {
string app_id = app.get_child_value (0).get_string ();
bool authed = app.get_child_value (1).get_variant ().get_child_value (0).get_boolean ();
var app_info = new DesktopAppInfo (app_id + ".desktop");

var app_row = new LocationRow (app_info, authed);
private Gtk.Widget create_widget_func (Object object) {
var app_permission = (AppPermission) object;
var app_row = new LocationRow (app_permission);
app_row.notify["authed"].connect (() => {
string[] permissions = {app_row.authed ? "EXACT" : "NONE", app_row.timestamp};
try {
permission_store.set_permission (PERMISSIONS_TABLE, true, PERMISSIONS_ID, app_permission.id, permissions);
} catch (Error e) {
critical (e.message);
}

app_row.active_changed.connect ((active) => {
uint32 level = get_app_level (app_id);
save_app_settings (app_id, active, level);
});

listbox.add (app_row);
}
}
});

private void load_remembered_apps () {
remembered_apps = location_settings.get_value ("remembered-apps");
remembered_apps_dict = new VariantDict (location_settings.get_value ("remembered-apps"));
return app_row;
}

private void save_app_settings (string desktop_id, bool authorized, uint32 accuracy_level) {
Variant[] tuple_vals = new Variant[2];
tuple_vals[0] = new Variant.boolean (authorized);
tuple_vals[1] = new Variant.uint32 (accuracy_level);
remembered_apps_dict.insert_value (desktop_id, new Variant.tuple (tuple_vals));
location_settings.set_value ("remembered-apps", remembered_apps_dict.end ());
load_remembered_apps ();
private void load_permissions () {
liststore.remove_all ();

try {
Variant permissions, data;
permission_store.lookup (PERMISSIONS_TABLE, PERMISSIONS_ID, out permissions, out data);

unowned string app_id;
unowned string[] app_permissions;
var iter = permissions.iterator ();
while (iter.next ("{&s^a&s}", out app_id, out app_permissions)) {
var app_permission = new AppPermission (
app_id,
app_permissions[0],
app_permissions[1]
);

liststore.append (app_permission);
}
} catch (Error e) {
critical (e.message);
}
}

private uint32 get_app_level (string desktop_id) {
return remembered_apps.lookup_value (desktop_id, GLib.VariantType.TUPLE).get_child_value (1).get_uint32 ();
}
private class AppPermission : Object {
public string id { get; construct; }
public string level { get; construct; }
public string timestamp { get; construct;}

public static bool location_agent_installed () {
var schemas = GLib.SettingsSchemaSource.get_default ();
if (schemas.lookup (LOCATION_AGENT_ID, true) != null) {
return true;
public AppPermission (string id, string level, string timestamp) {
Object (
id: id,
level: level,
timestamp: timestamp
);
}

return false;
}

private class LocationRow : AppRow {
public signal void active_changed (bool active);
public bool authed { get; construct; }
private Gtk.Switch active_switch;
public bool authed { get; construct set; }
public string timestamp { get; construct;}

public LocationRow (DesktopAppInfo app_info, bool authed) {
public LocationRow (AppPermission permission) {
Object (
app_info: app_info,
authed: authed
app_info: new GLib.DesktopAppInfo (permission.id + ".desktop"),
authed: permission.level != "NONE",
timestamp: permission.timestamp
);
}

construct {
active_switch = new Gtk.Switch ();
active_switch.halign = Gtk.Align.END;
active_switch.hexpand = true;
active_switch.tooltip_text = _("Allow %s to use location services".printf (app_info.get_display_name ()));
active_switch.valign = Gtk.Align.CENTER;
active_switch.active = authed;

main_grid.margin = 6;
var active_switch = new Gtk.Switch () {
halign = Gtk.Align.END,
hexpand = true,
tooltip_text = _("Allow %s to use location services".printf (app_info.get_display_name ())),
valign = Gtk.Align.CENTER
};

main_grid.margin_top = 6;
main_grid.margin_end = 6;
main_grid.margin_bottom = 6;
main_grid.margin_start = 6;
main_grid.attach (active_switch, 2, 0, 1, 2);
show_all ();

activate.connect (() => {
active_switch.active = false;
});

active_switch.notify ["active"].connect (() => {
active_changed (active_switch.active);
});
bind_property ("authed", active_switch, "active", BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
}

public void on_active_changed () {
active_switch.activate ();
authed = !authed;
}
}
}
14 changes: 6 additions & 8 deletions src/Widgets/ServiceList.vala
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,13 @@ public class ServiceList : Gtk.ListBox {
update_service_status (privacy_item, SecurityPrivacy.tracking.status_switch.active);
});

if (SecurityPrivacy.LocationPanel.location_agent_installed ()) {
var location_item = new ServiceItem ("preferences-system-privacy-location", "location", _("Location Services"));
add_service (location_item);
update_service_status (location_item, SecurityPrivacy.location.status_switch.active);
var location_item = new ServiceItem ("preferences-system-privacy-location", "location", _("Location Services"));
add_service (location_item);
update_service_status (location_item, SecurityPrivacy.location.status_switch.active);

SecurityPrivacy.location.status_switch.notify["active"].connect (() => {
update_service_status (location_item, SecurityPrivacy.location.status_switch.active);
});
}
SecurityPrivacy.location.status_switch.notify["active"].connect (() => {
update_service_status (location_item, SecurityPrivacy.location.status_switch.active);
});
}

private void update_service_status (ServiceItem service_item, bool service_status) {
Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ plug_files = files(
'Plug.vala',
'UFWHelpers.vala',
'ZGUtilities.vala',
'DBus/PermissionStore.vala',
'Views/FirewallPanel.vala',
'Views/HouseKeepingPanel.vala',
'Views/LockPanel.vala',
Expand Down

0 comments on commit d868a3f

Please sign in to comment.