location.py 3.76 KB
Newer Older
Philipp Hörist's avatar
Philipp Hörist committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Copyright (C) 2009-2014 Yann Leboulanger <asterix AT lagaule.org>
#
# This file is part of Gajim.
#
# Gajim 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; version 3 only.
#
# Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>.
16

André's avatar
André committed
17
import logging
Philipp Hörist's avatar
Philipp Hörist committed
18 19 20
from datetime import datetime

from gi.repository import GLib
Philipp Hörist's avatar
Philipp Hörist committed
21
from nbxmpp.structs import LocationData
22

23
from gajim.common import app
André's avatar
André committed
24

Philipp Hörist's avatar
Philipp Hörist committed
25
if app.is_installed('GEOCLUE'):
26
    from gi.repository import Geoclue  # pylint: disable=ungrouped-imports,no-name-in-module
André's avatar
André committed
27

28
log = logging.getLogger('gajim.c.dbus.location')
André's avatar
André committed
29

30 31

class LocationListener:
32
    _instance = None
André's avatar
André committed
33

34 35 36 37 38
    @classmethod
    def get(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance
39

40 41
    def __init__(self):
        self._data = {}
42 43
        self.location_info = {}
        self.simple = None
44

45
    # Note: do not remove third parameter `param`
46
    #       because notify signal expects three parameters
47
    def _on_location_update(self, simple, _param=None):
André's avatar
André committed
48 49 50 51 52 53 54 55 56 57 58 59 60
        location = simple.get_location()
        timestamp = location.get_property("timestamp")[0]
        lat = location.get_property("latitude")
        lon = location.get_property("longitude")
        alt = location.get_property("altitude")
        # in XEP-0080 it's horizontal accuracy
        acc = location.get_property("accuracy")

        # update data with info we just received
        self._data = {'lat': lat, 'lon': lon, 'alt': alt, 'accuracy': acc}
        self._data['timestamp'] = self._timestamp_to_utc(timestamp)
        self._send_location()

61
    def _on_simple_ready(self, _obj, result):
62
        try:
André's avatar
André committed
63
            self.simple = Geoclue.Simple.new_finish(result)
64
        except GLib.Error as error:
André's avatar
André committed
65
            log.warning("Could not enable geolocation: %s", error.message)
André's avatar
André committed
66 67 68 69 70 71 72 73 74
        else:
            self.simple.connect('notify::location', self._on_location_update)
            self._on_location_update(self.simple)

    def get_data(self):
        Geoclue.Simple.new("org.gajim.Gajim",
                           Geoclue.AccuracyLevel.EXACT,
                           None,
                           self._on_simple_ready)
75

76
    def start(self):
77
        self.location_info = {}
78
        self.get_data()
79

80
    def _send_location(self):
81
        accounts = app.connections.keys()
82
        for acct in accounts:
83
            if not app.account_is_available(acct):
84
                continue
85
            if not app.config.get_per('accounts', acct, 'publish_location'):
86
                continue
87
            if self.location_info == self._data:
88
                continue
89 90 91 92 93 94 95
            if 'timestamp' in self.location_info and 'timestamp' in self._data:
                last_data = self.location_info.copy()
                del last_data['timestamp']
                new_data = self._data.copy()
                del new_data['timestamp']
                if last_data == new_data:
                    continue
Philipp Hörist's avatar
Philipp Hörist committed
96 97
            app.connections[acct].get_module('UserLocation').set_location(
                LocationData(**self._data))
98
            self.location_info = self._data.copy()
99

100 101
    @staticmethod
    def _timestamp_to_utc(timestamp):
102 103 104
        time = datetime.utcfromtimestamp(timestamp)
        return time.strftime('%Y-%m-%dT%H:%MZ')

André's avatar
André committed
105

106
def enable():
Philipp Hörist's avatar
Philipp Hörist committed
107 108 109 110
    if not app.is_installed('GEOCLUE'):
        log.warning('GeoClue not installed')
        return

111 112
    listener = LocationListener.get()
    listener.start()