From 32a49b7846b4682364225b15bb5fa36848b23bf1 Mon Sep 17 00:00:00 2001 From: "Zeeshan Ali (Khattak)" Date: Thu, 13 Feb 2014 17:50:32 +0000 Subject: [PATCH] location: Provide a way to disable geolocation Now that we are indicating 'geolocation in use' to user, we better also provide at least a way to disable geolocation. Once this is in place, we can provide slightly better controls rather than simply on/off switch. https://bugzilla.gnome.org/show_bug.cgi?id=723684 --- js/ui/panel.js | 1 + js/ui/status/location.js | 130 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 6 deletions(-) diff --git a/js/ui/panel.js b/js/ui/panel.js index 62614ca33..cfa6fdb4e 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -853,6 +853,7 @@ const AggregateMenu = new Lang.Class({ if (this._bluetooth) { this.menu.addMenuItem(this._bluetooth.menu); } + this.menu.addMenuItem(this._location.menu); this.menu.addMenuItem(this._rfkill.menu); this.menu.addMenuItem(this._power.menu); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); diff --git a/js/ui/status/location.js b/js/ui/status/location.js index b14bd8abb..d83aaf675 100644 --- a/js/ui/status/location.js +++ b/js/ui/status/location.js @@ -1,18 +1,37 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Lang = imports.lang; +const Shell = imports.gi.Shell; const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; var GeoclueIface = ' \ \ \ + \ + \ + \ + \ \ '; const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface); +var AgentIface = ' \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +'; + const Indicator = new Lang.Class({ Name: 'LocationIndicator', Extends: PanelMenu.SystemIndicator, @@ -22,16 +41,58 @@ const Indicator = new Lang.Class({ this._indicator = this._addIndicator(); this._indicator.icon_name = 'find-location-symbolic'; - this._sync(); + this._syncIndicator(); + + this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true); + this._item.icon.icon_name = 'find-location-symbolic'; + + var credentials = new Gio.Credentials(); + var uid = credentials.get_unix_user(); + this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this); + this._agent.export(Gio.DBus.system, + '/org/freedesktop/GeoClue2/Agent/' + uid); + + this._item.status.text = _("On"); + this._onoffAction = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, this._onOnOffAction)); + + this.menu.addMenuItem(this._item); this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM, 'org.freedesktop.GeoClue2', 0, Lang.bind(this, this._connectToGeoclue), Lang.bind(this, this._onGeoclueVanished)); + + this._connectToGeoclue(); }, - _sync: function() { + get MaxAccuracyLevel() { + return this._maxAccuracyLevel; + }, + + set MaxAccuracyLevel(value) { + if (this._userSetAccuracy) + // If user set the max accuracy level, don't let geoclue override + return; + + this._setAccuracy (value, false); + }, + + // We (and geoclue) have currently no way to reliably identifying apps so + // for now, lets just authorize all apps as long as they provide a valid + // desktop ID. We also ensure they don't get more accuracy than global max. + AuthorizeApp: function(desktop_id, reqAccuracyLevel) { + var appSystem = Shell.AppSystem.get_default(); + var app = appSystem.lookup_app(desktop_id + ".desktop"); + if (app == null) { + return [false, 0]; + } + + var allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._maxAccuracyLevel); + return [true, allowedAccuracyLevel]; + }, + + _syncIndicator: function() { if (this._proxy == null) { this._indicator.visible = false; return; @@ -53,21 +114,78 @@ const Indicator = new Lang.Class({ }, _onProxyReady: function (proxy, error) { - this._connecting = false; if (error != null) { log (error.message); + this._userSetAccuracy = false; + this._connecting = false; return; } this._proxy = proxy; - this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync)); + this._proxy.connect('g-properties-changed', Lang.bind(this, this._syncIndicator)); - this._sync(); + if (!this._availableAccuracyLevel) { + this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel; + this._maxAccuracyLevel = this._availableAccuracyLevel; + } + + this._syncIndicator(); + + this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered)); + }, + + _onAgentRegistered: function(result, error) { + this._connecting = false; + this._notifyMaxAccuracyLevel(); + + if (error != null) + log (error.message); }, _onGeoclueVanished: function() { this._proxy = null; - this._sync(); + this._syncIndicator(); + }, + + _onOnOffAction: function() { + if (this._maxAccuracyLevel == 0) + this._setAccuracy (this._availableAccuracyLevel, true); + else + this._setAccuracy (0, true); + }, + + _setAccuracy: function(maxAccuracyLevel, userSet) { + this._maxAccuracyLevel = maxAccuracyLevel; + + if (this._maxAccuracyLevel == 0) { + this._item.status.text = _("Off"); + this._onoffAction.label.text = "Turn On"; + } else { + this._item.status.text = _("On"); + this._onoffAction.label.text = "Turn Off"; + } + + if (!userSet) + return; + + this._userSetAccuracy = true; + // Gotta ensure geoclue is up and we are registered as agent to it + // before we emit the notify for this property change. + if (!this._connectToGeoclue()) + this._notifyMaxAccuracyLevel(); + }, + + _notifyMaxAccuracyLevel: function() { + if (!this._userSetAccuracy) + return; + + var variant = new GLib.Variant('u', this._maxAccuracyLevel); + this._agent.emit_property_changed ('MaxAccuracyLevel', variant); + this._userSetAccuracy = false; } }); + +function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); +}