Merge lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1446770-location-settings into lp:~elementary-apps/switchboard-plug-security-privacy/trunk

Proposed by David Hewitt
Status: Merged
Approved by: Danielle Foré
Approved revision: 309
Merged at revision: 309
Proposed branch: lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1446770-location-settings
Merge into: lp:~elementary-apps/switchboard-plug-security-privacy/trunk
Diff against target: 332 lines (+248/-0)
4 files modified
src/CMakeLists.txt (+1/-0)
src/Plug.vala (+22/-0)
src/Views/LocationPanel.vala (+206/-0)
src/Widgets/ServiceList.vala (+19/-0)
To merge this branch: bzr merge lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1446770-location-settings
Reviewer Review Type Date Requested Status
Danielle Foré Approve
Review via email: mp+318428@code.launchpad.net

Commit message

Add Location panel to configure geoclue2 agent

Description of the change

Adds an extra panel to the plug to configure the Pantheon location agent (https://launchpad.net/pantheon-agent-geoclue2). The panel is written in such a way that it only registers itself if the agent is installed. If the user does not have the location agent installed, no change is made to the plug.

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote :

I wonder if "Location Services" makes more sense. Especially in context, I feel like "Location Services Are Disabled" makes more sense than "Location Is Disabled". Any thoughts on that?

Right now the way you're doing the disabled_panel doesn't actually hide the stuff below it, so you can see the borders get a little wonky because things are stacked on top of each other

304. By David Hewitt

Rename to 'Location Services' and switch to Gtk.Stack to show/hide disabled AlertView

305. By David Hewitt

Fix stack

306. By David Hewitt

Update copy

307. By David Hewitt

Remove double spacing between lines in AlertView

308. By David Hewitt

Merged trunk

Revision history for this message
Danielle Foré (danrabbit) wrote :

just a small inline comment. Can we make the title label in the alert view title case?

309. By David Hewitt

* Ensure location bits don't get set up if agent not installed
* Use title case in AlertView
* Merged trunk

Revision history for this message
Danielle Foré (danrabbit) wrote :

Looks good! Thanks for all of your work on this :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2017-02-27 22:58:22 +0000
3+++ src/CMakeLists.txt 2017-02-28 22:10:47 +0000
4@@ -21,6 +21,7 @@
5 Views/FirewallPanel.vala
6 Views/LockPanel.vala
7 Views/TrackPanel.vala
8+ Views/LocationPanel.vala
9
10 Widgets/AppChooser.vala
11 Widgets/AppRow.vala
12
13=== modified file 'src/Plug.vala'
14--- src/Plug.vala 2017-02-28 21:01:45 +0000
15+++ src/Plug.vala 2017-02-28 22:10:47 +0000
16@@ -25,14 +25,18 @@
17 public static Plug plug;
18 public static Gtk.LockButton lock_button;
19 public static Blacklist blacklist;
20+ public static LocationPanel location;
21 public static FirewallPanel firewall;
22
23 public class Plug : Switchboard.Plug {
24 Gtk.Grid main_grid;
25 Gtk.Stack stack;
26+
27 TrackPanel tracking;
28 ServiceList service_list;
29
30+ bool location_agent_installed = false;
31+
32 public Plug () {
33 Object (category: Category.PERSONAL,
34 code_name: Build.PLUGCODENAME,
35@@ -40,10 +44,16 @@
36 description: _("Configure firewall, screen lock, and activity information"),
37 icon: "preferences-system-privacy",
38 supported_settings: new Gee.TreeMap<string, string?> (null, null));
39+
40+ location_agent_installed = SecurityPrivacy.LocationPanel.location_agent_installed ();
41 supported_settings.set ("security", null);
42 supported_settings.set ("security/privacy", "tracking");
43 supported_settings.set ("security/firewall", "firewall");
44 supported_settings.set ("security/screensaver", "locking");
45+
46+ if (location_agent_installed) {
47+ supported_settings.set ("security/privacy/location", "location");
48+ }
49 plug = this;
50 }
51
52@@ -117,6 +127,11 @@
53 stack.add_titled (locking, "locking", _("Locking"));
54 stack.add_titled (firewall, "firewall", _("Firewall"));
55
56+ if (location_agent_installed) {
57+ location = new LocationPanel ();
58+ stack.add_titled (location, "location", _("Location Services"));
59+ }
60+
61 service_list = new ServiceList ();
62
63 var paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
64@@ -159,6 +174,10 @@
65 stack.set_visible_child_name ("firewall");
66 service_list.select_service_name ("firewall");
67 break;
68+ case "location":
69+ stack.set_visible_child_name ("location");
70+ service_list.select_service_name ("location");
71+ break;
72 }
73 }
74
75@@ -169,6 +188,9 @@
76 map.set ("%s → %s".printf (display_name, _("Locking")), "locking");
77 map.set ("%s → %s → %s".printf (display_name, _("Locking"), _("Privacy Mode")), "locking<sep>privacy-mode");
78 map.set ("%s → %s".printf (display_name, _("Firewall")), "firewall");
79+ if (location_agent_installed) {
80+ map.set ("%s → %s".printf (display_name, _("Location Services")), "location");
81+ }
82 return map;
83 }
84 }
85
86=== added file 'src/Views/LocationPanel.vala'
87--- src/Views/LocationPanel.vala 1970-01-01 00:00:00 +0000
88+++ src/Views/LocationPanel.vala 2017-02-28 22:10:47 +0000
89@@ -0,0 +1,206 @@
90+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
91+/*-
92+ * Copyright (c) 2017 elementary LLC.
93+ * Copyright (C) 2017 David Hewitt <davidmhewitt@gmail.com>
94+ *
95+ * This program is free software; you can redistribute it and/or modify
96+ * it under the terms of the GNU General Public License as published by
97+ * the Free Software Foundation; either version 3 of the License, or
98+ * (at your option) any later version.
99+ *
100+ * This program is distributed in the hope that it will be useful,
101+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
102+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+ * GNU General Public License for more details.
104+ *
105+ * You should have received a copy of the GNU General Public License
106+ * along with this program; if not, write to the Free Software Foundation,
107+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
108+ *
109+ * Authored by: David Hewitt <davidmhewitt@gmail.com>
110+ */
111+
112+public class SecurityPrivacy.LocationPanel : Gtk.Grid {
113+
114+ private GLib.Settings location_settings;
115+ private Variant remembered_apps;
116+ private VariantDict remembered_apps_dict;
117+ private Gtk.ListStore list_store;
118+ private Gtk.TreeView tree_view;
119+ private Gtk.Grid treeview_grid;
120+ private Gtk.Stack disabled_stack;
121+ public Gtk.Switch status_switch;
122+
123+ private enum Columns {
124+ AUTHORIZED,
125+ NAME,
126+ ICON,
127+ APP_ID,
128+ N_COLUMNS
129+ }
130+
131+ construct {
132+ column_spacing = 12;
133+ row_spacing = 6;
134+ margin = 12;
135+
136+ location_settings = new GLib.Settings ("org.pantheon.agent-geoclue2");
137+ disabled_stack = new Gtk.Stack ();
138+
139+ var status_icon = new Gtk.Image.from_icon_name ("find-location", Gtk.IconSize.DIALOG);
140+
141+ var status_label = new Gtk.Label (_("Location Services"));
142+ status_label.get_style_context ().add_class ("h2");
143+ status_label.hexpand = true;
144+ status_label.xalign = 0;
145+
146+ status_switch = new Gtk.Switch ();
147+ status_switch.valign = Gtk.Align.CENTER;
148+
149+ attach (status_icon, 0, 0, 1, 1);
150+ attach (status_label, 1, 0, 1, 1);
151+ attach (status_switch, 2, 0, 1, 1);
152+ attach (disabled_stack, 0, 1, 3, 1);
153+
154+ create_treeview ();
155+ create_disabled_panel ();
156+
157+ location_settings.bind ("location-enabled", status_switch, "active", SettingsBindFlags.DEFAULT);
158+ status_switch.notify["active"].connect (() => {
159+ update_stack_visible_child ();
160+ });
161+ location_settings.changed.connect((key) => {
162+ populate_app_treeview ();
163+ });
164+
165+ update_stack_visible_child ();
166+ }
167+
168+ private void update_stack_visible_child () {
169+ if (status_switch.active) {
170+ disabled_stack.set_visible_child_name ("enabled");
171+ } else {
172+ disabled_stack.set_visible_child_name ("disabled");
173+ }
174+ }
175+
176+ private void create_disabled_panel () {
177+ var disabled_frame = new Gtk.Frame (null);
178+ disabled_frame.expand = true;
179+
180+ var title = _("Location Services Are Disabled");
181+ var description = ("%s\n%s\n%s".printf (
182+ _("While location services are disabled, location requests from apps will be automatically rejected."),
183+ _("The additional functionality that location access provides in those apps will be affected."),
184+ _("This will not prevent apps from trying to determine your location based on IP address.")));
185+
186+ var alert = new Granite.Widgets.AlertView (title, description, "");
187+ alert.show_all ();
188+
189+ disabled_frame.add (alert);
190+ disabled_stack.add_named (disabled_frame, "disabled");
191+ disabled_frame.set_visible (true);
192+ }
193+
194+ private void create_treeview () {
195+ var locations_label = new Gtk.Label (_("Allow the apps below to determine your location"));
196+ locations_label.xalign = 0;
197+
198+ list_store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (bool),
199+ typeof (string), typeof (string), typeof (string));
200+
201+ tree_view = new Gtk.TreeView.with_model (list_store);
202+ tree_view.vexpand = true;
203+ tree_view.headers_visible = false;
204+ tree_view.activate_on_single_click = true;
205+
206+ var celltoggle = new Gtk.CellRendererToggle ();
207+ tree_view.row_activated.connect ((path, column) => {
208+ Value active;
209+ Gtk.TreeIter iter;
210+ list_store.get_iter (out iter, path);
211+ list_store.get_value (iter, Columns.AUTHORIZED, out active);
212+ var is_active = !active.get_boolean ();
213+ list_store.set (iter, Columns.AUTHORIZED, is_active);
214+ Value app_id;
215+ list_store.get_value (iter, Columns.APP_ID, out app_id);
216+
217+ uint32 level = get_app_level (app_id.get_string ());
218+ save_app_settings (app_id.get_string (), is_active, level);
219+ });
220+
221+ var cell = new Gtk.CellRendererText ();
222+ var cellpixbuf = new Gtk.CellRendererPixbuf ();
223+ cellpixbuf.stock_size = Gtk.IconSize.DND;
224+ tree_view.insert_column_with_attributes (-1, "", celltoggle, "active", Columns.AUTHORIZED);
225+ tree_view.insert_column_with_attributes (-1, "", cellpixbuf, "icon-name", Columns.ICON);
226+ tree_view.insert_column_with_attributes (-1, "", cell, "markup", Columns.NAME);
227+
228+ populate_app_treeview ();
229+
230+ var scrolled = new Gtk.ScrolledWindow (null, null);
231+ scrolled.shadow_type = Gtk.ShadowType.IN;
232+ scrolled.expand = true;
233+ scrolled.add (tree_view);
234+
235+ treeview_grid = new Gtk.Grid ();
236+ treeview_grid.margin_top = 12;
237+ treeview_grid.row_spacing = 6;
238+ treeview_grid.attach (locations_label, 0, 0, 1, 1);
239+ treeview_grid.attach (scrolled, 0, 1, 1, 1);
240+
241+ disabled_stack.add_named (treeview_grid, "enabled");
242+ treeview_grid.set_visible (true);
243+ }
244+
245+ private void populate_app_treeview () {
246+ load_remembered_apps ();
247+ Gtk.TreePath? current_selection;
248+ Gtk.TreeViewColumn? current_column;
249+ tree_view.get_cursor (out current_selection, out current_column);
250+
251+ list_store.clear ();
252+ foreach (var app in remembered_apps) {
253+ string app_id = app.get_child_value (0).get_string ();
254+ bool authed = app.get_child_value (1).get_variant ().get_child_value (0).get_boolean ();
255+ var app_info = new DesktopAppInfo (app_id + ".desktop");
256+ add_liststore_item (list_store, authed, app_info.get_display_name (), app_info.get_icon ().to_string (), app_id);
257+ }
258+
259+ tree_view.set_cursor (current_selection, current_column, false);
260+ }
261+
262+ private void add_liststore_item (Gtk.ListStore list_store, bool active, string name, string icon, string app_id) {
263+ Gtk.TreeIter iter;
264+ list_store.append (out iter);
265+ list_store.set (iter, Columns.AUTHORIZED, active, Columns.NAME, name,
266+ Columns.ICON, icon, Columns.APP_ID, app_id);
267+ }
268+
269+ private void load_remembered_apps () {
270+ remembered_apps = location_settings.get_value ("remembered-apps");
271+ remembered_apps_dict = new VariantDict (location_settings.get_value ("remembered-apps"));
272+ }
273+
274+ private void save_app_settings (string desktop_id, bool authorized, uint32 accuracy_level) {
275+ Variant[2] tuple_vals = new Variant[2];
276+ tuple_vals[0] = new Variant.boolean (authorized);
277+ tuple_vals[1] = new Variant.uint32 (accuracy_level);
278+ remembered_apps_dict.insert_value (desktop_id, new Variant.tuple (tuple_vals));
279+ location_settings.set_value ("remembered-apps", remembered_apps_dict.end ());
280+ load_remembered_apps ();
281+ }
282+
283+ private uint32 get_app_level (string desktop_id) {
284+ return remembered_apps.lookup_value (desktop_id, GLib.VariantType.TUPLE).get_child_value (1).get_uint32 ();
285+ }
286+
287+ public static bool location_agent_installed () {
288+ var schemas = GLib.SettingsSchemaSource.get_default ();
289+ if (schemas.lookup ("org.pantheon.agent-geoclue2", true) != null) {
290+ return true;
291+ }
292+
293+ return false;
294+ }
295+}
296
297=== modified file 'src/Widgets/ServiceList.vala'
298--- src/Widgets/ServiceList.vala 2017-02-28 21:01:45 +0000
299+++ src/Widgets/ServiceList.vala 2017-02-28 22:10:47 +0000
300@@ -6,6 +6,8 @@
301 selection_mode: Gtk.SelectionMode.SINGLE);
302 }
303
304+ ServiceItem? location_item;
305+
306 construct {
307 var privacy_item = new ServiceItem ("document-open-recent", "tracking", _("History"));
308 var lock_item = new ServiceItem ("system-lock-screen", "locking", _("Locking"));
309@@ -22,6 +24,23 @@
310 firewall_item.status = ServiceItem.Status.DISABLED;
311 }
312 });
313+
314+ if (SecurityPrivacy.LocationPanel.location_agent_installed ()) {
315+ location_item = new ServiceItem ("find-location", "location", _("Location Services"));
316+ add_service (location_item);
317+ update_location_status ();
318+ SecurityPrivacy.location.status_switch.notify["active"].connect (() => {
319+ update_location_status ();
320+ });
321+ }
322+ }
323+
324+ private void update_location_status () {
325+ if (SecurityPrivacy.location.status_switch.active) {
326+ location_item.status = ServiceItem.Status.ENABLED;
327+ } else {
328+ location_item.status = ServiceItem.Status.DISABLED;
329+ }
330 }
331
332 public void add_service (ServiceItem service) {

Subscribers

People subscribed via source and target branches

to all changes: