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
This commit is contained in:
Zeeshan Ali (Khattak) 2014-02-13 17:50:32 +00:00
parent 12ef034b7b
commit 32a49b7846
2 changed files with 125 additions and 6 deletions

View File

@ -853,6 +853,7 @@ const AggregateMenu = new Lang.Class({
if (this._bluetooth) { if (this._bluetooth) {
this.menu.addMenuItem(this._bluetooth.menu); this.menu.addMenuItem(this._bluetooth.menu);
} }
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu); this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu); this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());

View File

@ -1,18 +1,37 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
var GeoclueIface = '<node> \ var GeoclueIface = '<node> \
<interface name="org.freedesktop.GeoClue2.Manager"> \ <interface name="org.freedesktop.GeoClue2.Manager"> \
<property name="InUse" type="b" access="read"/> \ <property name="InUse" type="b" access="read"/> \
<property name="AvailableAccuracyLevel" type="u" access="read"/> \
<method name="AddAgent"> \
<arg name="id" type="s" direction="in"/> \
</method> \
</interface> \ </interface> \
</node>'; </node>';
const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface); const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
var AgentIface = '<node> \
<interface name="org.freedesktop.GeoClue2.Agent"> \
<property name="MaxAccuracyLevel" type="u" access="readwrite"/> \
<method name="AuthorizeApp"> \
<arg name="desktop_id" type="s" direction="in"/> \
<arg name="req_accuracy_level" type="u" direction="in"/> \
<arg name="authorized" type="b" direction="out"/> \
<arg name="allowed_accuracy_level" type="u" direction="out"/> \
</method> \
</interface> \
</node>';
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'LocationIndicator', Name: 'LocationIndicator',
Extends: PanelMenu.SystemIndicator, Extends: PanelMenu.SystemIndicator,
@ -22,16 +41,58 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator(); this._indicator = this._addIndicator();
this._indicator.icon_name = 'find-location-symbolic'; 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, this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
'org.freedesktop.GeoClue2', 'org.freedesktop.GeoClue2',
0, 0,
Lang.bind(this, this._connectToGeoclue), Lang.bind(this, this._connectToGeoclue),
Lang.bind(this, this._onGeoclueVanished)); 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) { if (this._proxy == null) {
this._indicator.visible = false; this._indicator.visible = false;
return; return;
@ -53,21 +114,78 @@ const Indicator = new Lang.Class({
}, },
_onProxyReady: function (proxy, error) { _onProxyReady: function (proxy, error) {
this._connecting = false;
if (error != null) { if (error != null) {
log (error.message); log (error.message);
this._userSetAccuracy = false;
this._connecting = false;
return; return;
} }
this._proxy = proxy; 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() { _onGeoclueVanished: function() {
this._proxy = null; 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));
}