From 76bde9959fc92b766e8dca5338bb0c84ec8136f5 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Mon, 11 May 2015 00:05:39 +0200 Subject: First version which seems to make some sense. Now testing! --- extension.js | 260 +++++++++++++++++++++++++++++++++++++++++++++++++-------- geoclue.js | 82 ------------------ metadata.json | 3 +- place.js | 230 -------------------------------------------------- stylesheet.css | 8 -- 5 files changed, 228 insertions(+), 355 deletions(-) delete mode 100755 geoclue.js delete mode 100644 place.js delete mode 100644 stylesheet.css diff --git a/extension.js b/extension.js index 260aef7..bc89de1 100644 --- a/extension.js +++ b/extension.js @@ -1,53 +1,245 @@ - +/* jshint moz: true, multistr: true */ +/* global imports */ +const Gio = imports.gi.Gio; +const Mainloop = imports.mainloop; +const GObject = imports.gi.GObject; +const Lang = imports.lang; const St = imports.gi.St; const Main = imports.ui.main; -const Tweener = imports.ui.tweener; -let text, button; +const AccuracyLevel = { + COUNTRY: 1, + CITY: 4, + NEIGHBORHOOD: 5, + STREET: 6, + EXACT: 8 +}; -function _hideHello() { - Main.uiGroup.remove_actor(text); - text = null; -} +const API_URL = 'http://api.open-notify.org/iss-now.json'; +// The minimal distance in km for icon to be made visible +const SOUGHT_DISTANCE = 500; +const CHECK_INTERVAL = 5 * 60; // how often (in sec) the check for ISS is done + +const ManagerIface = ' \ + \ + \ + \ + \ + \ +'; +const ManagerProxy = Gio.DBusProxy.makeProxyWrapper(ManagerIface); + +const ClientInterface = ' \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +'; +const ClientProxy = Gio.DBusProxy.makeProxyWrapper(ClientInterface); + +const LocationInterface = ' \ + \ + \ + \ + \ + \ + \ +'; +const LocationProxy = Gio.DBusProxy.makeProxyWrapper(LocationInterface); -function _showHello() { - if (!text) { - text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" }); - Main.uiGroup.add_actor(text); +// global variables +let text, button, label, client; + +const ISS_Above = new GObject.Class({ + Name: 'ISS_Above', + Extends: GObject.Object, + Signals: { + 'processed_data': { + param_types: [ + // GObject.TYPE_INT + ] } + }, - text.opacity = 255; + _init: function(params) { + this.parent(params); - let monitor = Main.layoutManager.primaryMonitor; + this.managerProxy = new ManagerProxy(Gio.DBus.system, + 'org.freedesktop.GeoClue2', '/org/freedesktop/GeoClue2/Manager'); - text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2), - monitor.y + Math.floor(monitor.height / 2 - text.height / 2)); + [this.clientAddr] = this.managerProxy.GetClientSync(); - Tweener.addTween(text, - { opacity: 0, - time: 2, - transition: 'easeOutQuad', - onComplete: _hideHello }); -} + this.current_location = null; + this.iss_coords = null; + this.task_queue = 2; // number of async process to complete + this.timeout_source = null; + + this.clientProxy = new ClientProxy(Gio.DBus.system, + 'org.freedesktop.GeoClue2', this.clientAddr); + this.clientProxy.DesktopId = 'gnome-shell-ISS_Above'; + this.clientProxy.DistanceThreshold = 10000; + this.clientProxy.RequestedAccuracyLevel = AccuracyLevel.EXACT; + this.clientProxy.connectSignal('LocationUpdated', + Lang.bind(this, this.onLocationUpdated)); + this.clientProxy.StartRemote(); + this.connect('processed_data', Lang.bind(this, this.processResult)); + }, + + run: function() { + this.getISScoords(function () { + print('run: processing new coords'); + this.task_queue = 1; + this.processResult(); + }); + }, + + getISScoords: function (cb_process) { + let self = this; + let iss_api = Gio.file_new_for_uri(API_URL); + iss_api.load_contents_async(null, function(iss_api, result) { + self.iss_coords = JSON.parse(iss_api.load_contents_finish(result)[1]); + print('self.iss_coords = ' + self.iss_coords.toSource()); + if (cb_process !== undefined) { + cb_process(); + } + else { + self.emit('processed_data'); + } + }); + }, + + onLocationUpdated: function (proxy, sender, [oldPath, newPath]) { + let geoclueLocation = new LocationProxy(Gio.DBus.system, + "org.freedesktop.GeoClue2", + newPath); + + // Yes, I take both null and undefined + if (geoclueLocation.Latitude != null) { + this.current_location = { + Latitude: geoclueLocation.Latitude, + Longitude: geoclueLocation.Longitude, + Accuracy: geoclueLocation.Accuracy, + Description: geoclueLocation.Description + }; + } + + this.emit('processed_data'); + }, + + /** + * Calculate distance in km of two geographical points + * + * @param lat1 Number latitude of the first point + * @param long1 Number longitude of the first point + * @param lat2 Number latitude of the second point + * @param long2 Number longitude of the second point + * @return Number distance in km + * + * Described on https://en.wikipedia.org/wiki/Haversine_formula + */ + get_distance: function(lat1, long1, lat2, long2) { + // Mean radius of the Earth in km + const EARTH_R = 6371; + const PI_180 = Math.PI / 180; + + function haversin(fi) { + return (1 - Math.cos(fi)) / 2; + } + + lat1 = lat1 * PI_180; + long1 = long1 * PI_180; + lat2 = lat2 * PI_180; + long2 = long2 * PI_180; + + let dist = 2 * EARTH_R * + Math.asin(Math.sqrt(haversin(lat2 - lat1) + + Math.cos(lat1) * Math.cos(lat2) * haversin(long2 - long1))); + + let test_haversin = haversin(dist / EARTH_R); + if (test_haversin < 1) { + return dist.toFixed(3); + } + else { + throw new Error('haversine(d/r) cannot be over 1, but it is ' + + test_haversin); + } + }, + + processResult: function() { + print('processResult, start: task_queue = ' + this.task_queue); + this.task_queue--; + + if (this.task_queue === 0) { + print('processResult: our location: ' + + this.current_location.toSource()); + print('processResult: iss coordinates: ' + + this.iss_coords.toSource()); + + let dist = this.get_distance(this.current_location.Latitude, + this.current_location.Longitude, + this.iss_coords.iss_position.latitude, + this.iss_coords.iss_position.longitude); + print('ISS is ' + dist + ' km away.'); + + // https://developer.gnome.org/clutter/stable/ClutterActor.html + if (dist < SOUGHT_DISTANCE) { + // Make the label text completely opaque + label.set_opacity(255); + print('Make label opaque'); + } + else { + // Make the label text completely transparent + label.set_opacity(0); + print('Make label transparent'); + } + } + } +}); + +// END of ISS_Above object definition function init() { - button = new St.Bin({ style_class: 'panel-button', - reactive: true, - can_focus: true, - x_fill: true, - y_fill: false, - track_hover: true }); - let icon = new St.Icon({ icon_name: 'system-run-symbolic', - style_class: 'system-status-icon' }); - - button.set_child(icon); - button.connect('button-press-event', _showHello); } function enable() { - Main.panel._rightBox.insert_child_at_index(button, 0); + print('ENABLING ISS Above'); + button = new St.Bin({ style_class: 'panel-button', + reactive: true, + can_focus: true, + x_fill: true, + y_fill: false, + track_hover: true }); + // http://blog.fpmurphy.com/2011/04/replace-gnome-shell-activities-text-string-with-icon.html + label = new St.Label({ text: 'ISS' }); + print('button is ' + button); + + button.set_child(label); + label.set_opacity(0); + + Main.panel._rightBox.insert_child_at_index(button, 0); + + client = new ISS_Above(); + client.getISScoords(); + + client.timeout_source = Mainloop.timeout_add_seconds( + CHECK_INTERVAL, client.run); + + Mainloop.run(); } function disable() { - Main.panel._rightBox.remove_child(button); + print('DISABLING ISS Above'); + Main.panel._rightBox.remove_child(button); + Mainloop.source_remove(client.timeout_source); + + Mainloop.quit(); } diff --git a/geoclue.js b/geoclue.js deleted file mode 100755 index bcd0775..0000000 --- a/geoclue.js +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/gjs -// Requires .desktop file installed in $XDG_DATA_DIRS/applications - -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const Json = imports.gi.Json; -const Lang = imports.lang; -const Mainloop = imports.mainloop; - -const AccuracyLevel = { - COUNTRY: 1, - CITY: 4, - NEIGHBORHOOD: 5, - STREET: 6, - EXACT: 8 -}; - -const ManagerIface = ' \ - \ - \ - \ - \ - \ -'; -const ManagerProxy = Gio.DBusProxy.makeProxyWrapper(ManagerIface); - -const ClientInterface = ' \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -'; -const ClientProxy = Gio.DBusProxy.makeProxyWrapper(ClientInterface); - -const LocationInterface = ' \ - \ - \ - \ - \ - \ - \ -'; -const LocationProxy = Gio.DBusProxy.makeProxyWrapper(LocationInterface); - - -function onLocationUpdated(proxy, sender, [oldPath, newPath]) { - let geoclueLocation = new LocationProxy(Gio.DBus.system, - "org.freedesktop.GeoClue2", - newPath); - print('location:'); - print('\tlatitude = ' + geoclueLocation.Latitude); - print('\tlongitude = ' + geoclueLocation.Longitude); - print('\taccuracy = ' + geoclueLocation.Accuracy); - print('\tdescription = ' + geoclueLocation.Description); - //this._updateLocation(location); - Mainloop.quit(); -} - -let _managerProxy = new ManagerProxy(Gio.DBus.system, - 'org.freedesktop.GeoClue2', '/org/freedesktop/GeoClue2/Manager'); - -let [clientAddr] = _managerProxy.GetClientSync(); - -let clientProxy = new ClientProxy(Gio.DBus.system, - 'org.freedesktop.GeoClue2', clientAddr); -clientProxy.DesktopId = 'gnome-shell-ISS_Above'; -clientProxy.DistanceThreshold = 10000; -clientProxy.RequestedAccuracyLevel = AccuracyLevel.EXACT; -let updatedId = clientProxy.connectSignal('LocationUpdated', - onLocationUpdated); -clientProxy.StartRemote(); - -Mainloop.run(); diff --git a/metadata.json b/metadata.json index 36960bd..2eb1f76 100644 --- a/metadata.json +++ b/metadata.json @@ -4,5 +4,6 @@ ], "uuid": "ISS_Above@mcepl.cepl.eu", "name": "ISS Above", - "description": "Shows an icon when ISS is above you (+- 100km)" + "description": "Shows an icon when ISS is above you (+- 100km)", + "url": "https://gitlab.com/mcepl/gnome-shell-extension-ISS_Above/" } diff --git a/place.js b/place.js deleted file mode 100644 index 91dd178..0000000 --- a/place.js +++ /dev/null @@ -1,230 +0,0 @@ -/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */ -/* vim: set et ts=4 sw=4: */ -/* - * Copyright (c) 2014 Jonas Danielsson - * - * GNOME Maps is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * GNOME Maps is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with GNOME Maps; if not, see . - * - * Author: Jonas Danielsson - */ - -const Geocode = imports.gi.GeocodeGlib; -const GLib = imports.gi.GLib; -const Lang = imports.lang; -const Translations = imports.translations; - -const Place = new Lang.Class({ - Name: 'Place', - Extends: Geocode.Place, - - _init: function(params) { - this._population = params.population; - delete params.population; - - this._wiki = params.wiki; - delete params.wiki; - - this._openingHours = params.openingHours; - delete params.openingHours; - - this._wheelchair = params.wheelchair; - delete params.wheelchair; - - if (params.place) { - params = { osm_id: params.place.osm_id, - osm_type: params.place.osm_type, - name: params.place.name, - location: params.place.location, - bounding_box: params.place.bounding_box, - place_type: params.place.place_type, - street_address: params.place.street_address, - street: params.place.street, - building: params.place.building, - postal_code: params.place.postal_code, - area: params.place.area, - town: params.place.town, - state: params.place.state, - county: params.place.county, - country: params.place.country, - country_code: params.place.contry_code, - continent: params.place.continent }; - } - - for (let prop in params) - if (!params[prop]) - delete params[prop]; - - this.parent(params); - }, - - get uniqueID() { - return this.osm_type + '-' + this.osm_id; - }, - - set population(v) { - this._population = v; - }, - - get population() { - return this._population; - }, - - set wiki(v) { - this._wiki = v; - }, - - get wiki() { - return this._wiki; - }, - - set openingHours(v) { - this._openingHours = v; - }, - - get openingHours() { - return this._openingHours; - }, - - get openingHoursTranslated() { - return Translations.translateOpeningHours(this._openingHours); - }, - - set wheelchair(v) { - this._wheelchair = v; - }, - - get wheelchair() { - return this._wheelchair; - }, - - get wheelchairTranslated() { - return this._translateWheelchair(this._wheelchair); - }, - - _translateWheelchair: function(string) { - switch(string) { - /* Translators: - * This means wheelchairs have full unrestricted access. - */ - case 'yes': return _("yes"); - - /* Translators: - * This means wheelchairs have partial access (e.g some areas - * can be accessed and others not, areas requiring assistance - * by someone pushing up a steep gradient). - */ - case 'limited': return _("limited"); - - /* Translators: - * This means wheelchairs have no unrestricted access - * (e.g. stair only access). - */ - case 'no': return _("no"); - - /* Translators: - * This means that the way or area is designated or purpose built - * for wheelchairs (e.g. elevators designed for wheelchair access - * only). This is rarely used. - */ - case 'designated': return _("designated"); - - default: return null; - } - }, - - - toJSON: function() { - let bounding_box = null; - - if (this.bounding_box) { - bounding_box = { top: this.bounding_box.top, - bottom: this.bounding_box.bottom, - left: this.bounding_box.left, - right: this.bounding_box.right }; - } - - let location = { latitude: this.location.latitude, - longitude: this.location.longitude, - altitude: this.location.altitude, - accuracy: this.location.accuracy }; - - return { id: this.osm_id, - osm_type: this.osm_type, - name: this.name, - bounding_box: bounding_box, - this_type: this.this_type, - location: location, - street_address: this.street_address, - street: this.street, - building: this.building, - postal_code: this.postal_code, - area: this.area, - town: this.town, - state: this.state, - county: this.county, - country: this.country, - country_code: this.contry_code, - continent: this.continent, - population: this.population, - wiki: this.wiki, - wheelchair: this.wheelchair, - openingHours: this.openingHours }; - }, - - match: function(searchString) { - let name = this.name; - - searchString = GLib.utf8_normalize(searchString, -1, GLib.NormalizeMode.ALL); - if (searchString === null) - return false; - - if (searchString.length === 0) - return true; - - name = GLib.utf8_normalize(name, -1, GLib.NormalizeMode.ALL); - if (name === null) - return false; - - return name.toLowerCase().search(searchString.toLowerCase()) !== -1; - } -}); - -Place.fromJSON = function(obj) { - let props = { }; - - for (let key in obj) { - let prop = obj[key]; - - switch(key) { - case 'id': - props.osm_id = prop; - break; - - case 'location': - props.location = new Geocode.Location(prop); - break; - - case 'bounding_box': - if (prop) - props.bounding_box = new Geocode.BoundingBox(prop); - break; - - default: - if (prop !== null && prop !== undefined) - props[key] = prop; - break; - } - } - return new Place(props); -}; diff --git a/stylesheet.css b/stylesheet.css deleted file mode 100644 index 432a360..0000000 --- a/stylesheet.css +++ /dev/null @@ -1,8 +0,0 @@ -.helloworld-label { - font-size: 36px; - font-weight: bold; - color: #ffffff; - background-color: rgba(10,10,10,0.7); - border-radius: 5px; - padding: .5em; -} -- cgit