2 * Copyright (C) 2009 Collabora Ltd.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301 USA
19 * Authors: Pierre-Luc Beaudoin <pierre-luc.beaudoin@collabora.co.uk>
23 #include "empathy-location-manager.h"
25 #include <telepathy-glib/telepathy-glib-dbus.h>
27 #include <tp-account-widgets/tpaw-time.h>
29 #include "empathy-gsettings.h"
30 #include "empathy-location.h"
31 #include "empathy-geoclue-helper.h"
33 #define DEBUG_FLAG EMPATHY_DEBUG_LOCATION
34 #include "empathy-debug.h"
36 /* Seconds before updating the location */
38 static EmpathyLocationManager
*location_manager
= NULL
;
48 struct _EmpathyLocationManagerPrivate
{
49 GeoclueStatus geoclue_status
;
50 /* Contains the location to be sent to accounts. Geoclue is used
51 * to populate it. This HashTable uses Telepathy's style (string,
52 * GValue). Keys are defined in empathy-location.h
56 GSettings
*gsettings_loc
;
58 gboolean reduce_accuracy
;
59 TpAccountManager
*account_manager
;
60 EmpathyGeoclueHelper
*geoclue
;
62 /* The idle id for publish_on_idle func */
66 G_DEFINE_TYPE (EmpathyLocationManager
, empathy_location_manager
, G_TYPE_OBJECT
);
69 location_manager_constructor (GType type
,
70 guint n_construct_params
,
71 GObjectConstructParam
*construct_params
)
75 if (location_manager
== NULL
)
77 retval
= G_OBJECT_CLASS (empathy_location_manager_parent_class
)->constructor
78 (type
, n_construct_params
, construct_params
);
80 location_manager
= EMPATHY_LOCATION_MANAGER (retval
);
81 g_object_add_weak_pointer (retval
, (gpointer
) &location_manager
);
85 retval
= g_object_ref (location_manager
);
92 location_manager_dispose (GObject
*object
)
94 EmpathyLocationManager
*self
= (EmpathyLocationManager
*) object
;
95 void (*dispose
) (GObject
*) =
96 G_OBJECT_CLASS (empathy_location_manager_parent_class
)->dispose
;
98 tp_clear_object (&self
->priv
->account_manager
);
99 tp_clear_object (&self
->priv
->gsettings_loc
);
100 tp_clear_pointer (&self
->priv
->location
, g_hash_table_unref
);
107 empathy_location_manager_class_init (EmpathyLocationManagerClass
*class)
109 GObjectClass
*object_class
;
111 object_class
= G_OBJECT_CLASS (class);
113 object_class
->constructor
= location_manager_constructor
;
114 object_class
->dispose
= location_manager_dispose
;
116 g_type_class_add_private (object_class
, sizeof (EmpathyLocationManagerPrivate
));
120 publish_location_cb (TpConnection
*connection
,
123 GObject
*weak_object
)
126 DEBUG ("Error setting location: %s", error
->message
);
130 publish_location (EmpathyLocationManager
*self
,
132 gboolean force_publication
)
134 guint connection_status
= -1;
139 if (!force_publication
)
141 if (!g_settings_get_boolean (self
->priv
->gsettings_loc
,
142 EMPATHY_PREFS_LOCATION_PUBLISH
))
146 connection_status
= tp_connection_get_status (conn
, NULL
);
148 if (connection_status
!= TP_CONNECTION_STATUS_CONNECTED
)
151 DEBUG ("Publishing %s location to connection %p",
152 (g_hash_table_size (self
->priv
->location
) == 0 ? "empty" : ""),
155 tp_cli_connection_interface_location_call_set_location (conn
, -1,
156 self
->priv
->location
, publish_location_cb
, NULL
, NULL
, G_OBJECT (self
));
161 EmpathyLocationManager
*self
;
162 gboolean force_publication
;
166 publish_to_all_am_prepared_cb (GObject
*source_object
,
167 GAsyncResult
*result
,
170 TpAccountManager
*manager
= TP_ACCOUNT_MANAGER (source_object
);
171 PublishToAllData
*data
= user_data
;
173 GError
*error
= NULL
;
175 if (!tp_proxy_prepare_finish (manager
, result
, &error
))
177 DEBUG ("Failed to prepare account manager: %s", error
->message
);
178 g_error_free (error
);
182 accounts
= tp_account_manager_dup_valid_accounts (manager
);
183 for (l
= accounts
; l
; l
= l
->next
)
185 TpConnection
*conn
= tp_account_get_connection (TP_ACCOUNT (l
->data
));
188 publish_location (data
->self
, conn
, data
->force_publication
);
190 g_list_free_full (accounts
, g_object_unref
);
193 g_object_unref (data
->self
);
194 g_slice_free (PublishToAllData
, data
);
198 publish_to_all_connections (EmpathyLocationManager
*self
,
199 gboolean force_publication
)
201 PublishToAllData
*data
;
203 data
= g_slice_new0 (PublishToAllData
);
204 data
->self
= g_object_ref (self
);
205 data
->force_publication
= force_publication
;
207 tp_proxy_prepare_async (self
->priv
->account_manager
, NULL
,
208 publish_to_all_am_prepared_cb
, data
);
212 publish_on_idle (gpointer user_data
)
214 EmpathyLocationManager
*manager
= EMPATHY_LOCATION_MANAGER (user_data
);
216 manager
->priv
->timeout_id
= 0;
217 publish_to_all_connections (manager
, TRUE
);
222 new_connection_cb (TpAccount
*account
,
226 gchar
*dbus_error_name
,
230 EmpathyLocationManager
*self
= user_data
;
233 conn
= tp_account_get_connection (account
);
235 DEBUG ("New connection %p", conn
);
237 /* Don't publish if it is already planned (ie startup) */
238 if (self
->priv
->timeout_id
== 0)
240 publish_location (EMPATHY_LOCATION_MANAGER (self
), conn
,
246 update_location (EmpathyLocationManager
*self
,
247 GClueLocation
*proxy
)
249 gdouble latitude
, longitude
, accuracy
;
253 latitude
= gclue_location_get_latitude (proxy
);
254 longitude
= gclue_location_get_longitude (proxy
);
255 accuracy
= gclue_location_get_accuracy (proxy
);
256 desc
= gclue_location_get_description (proxy
);
258 DEBUG ("Location updated: (%f %f) accuracy: %f (%s)",
259 latitude
, longitude
, accuracy
, desc
);
261 if (self
->priv
->reduce_accuracy
)
263 /* Truncate at 1 decimal place */
264 latitude
= ((int) (latitude
* 10)) / 10.0;
265 longitude
= ((int) (longitude
* 10)) / 10.0;
269 /* Include the description only if we are not asked to reduce the
270 * accuracy as it can contains a pretty specific description of the
272 tp_asv_set_string (self
->priv
->location
, EMPATHY_LOCATION_DESCRIPTION
,
276 tp_asv_set_double (self
->priv
->location
, EMPATHY_LOCATION_LAT
, latitude
);
277 tp_asv_set_double (self
->priv
->location
, EMPATHY_LOCATION_LON
, longitude
);
278 tp_asv_set_double (self
->priv
->location
, EMPATHY_LOCATION_ACCURACY
, accuracy
);
280 timestamp
= tpaw_time_get_current ();
281 tp_asv_set_int64 (self
->priv
->location
, EMPATHY_LOCATION_TIMESTAMP
,
284 if (self
->priv
->timeout_id
== 0)
285 self
->priv
->timeout_id
= g_timeout_add_seconds (TIMEOUT
, publish_on_idle
,
290 location_changed_cb (EmpathyGeoclueHelper
*geoclue
,
291 GClueLocation
*location
,
292 EmpathyLocationManager
*self
)
294 update_location (self
, location
);
298 geoclue_new_cb (GObject
*source
,
299 GAsyncResult
*result
,
302 EmpathyLocationManager
*self
= EMPATHY_LOCATION_MANAGER (user_data
);
303 GError
*error
= NULL
;
304 GClueLocation
*location
;
306 self
->priv
->geoclue
= empathy_geoclue_helper_new_started_finish (result
,
309 if (self
->priv
->geoclue
== NULL
)
311 DEBUG ("Failed to create Geoclue client: %s", error
->message
);
312 g_error_free (error
);
313 self
->priv
->geoclue_status
= GEOCLUE_FAILED
;
317 self
->priv
->geoclue_status
= GEOCLUE_STARTED
;
319 g_signal_connect_object (self
->priv
->geoclue
, "location-changed",
320 G_CALLBACK (location_changed_cb
), self
, 0);
322 location
= empathy_geoclue_helper_get_location (self
->priv
->geoclue
);
323 if (location
!= NULL
)
324 update_location (self
, location
);
328 setup_geoclue (EmpathyLocationManager
*self
)
330 switch (self
->priv
->geoclue_status
)
333 g_assert (self
->priv
->geoclue
== NULL
);
334 self
->priv
->geoclue_status
= GEOCLUE_STARTING
;
335 empathy_geoclue_helper_new_started_async (0, geoclue_new_cb
, self
);
337 case GEOCLUE_STARTED
:
338 case GEOCLUE_STARTING
:
345 publish_cb (GSettings
*gsettings_loc
,
349 EmpathyLocationManager
*self
= EMPATHY_LOCATION_MANAGER (user_data
);
351 DEBUG ("Publish Conf changed");
353 if (g_settings_get_boolean (gsettings_loc
, key
))
355 setup_geoclue (self
);
359 /* As per XEP-0080: send an empty location to have remove current
360 * location from the servers
362 g_hash_table_remove_all (self
->priv
->location
);
363 publish_to_all_connections (self
, TRUE
);
365 g_clear_object (&self
->priv
->geoclue
);
366 self
->priv
->geoclue_status
= GEOCLUE_NONE
;
371 reduce_accuracy_cb (GSettings
*gsettings_loc
,
375 EmpathyLocationManager
*self
= EMPATHY_LOCATION_MANAGER (user_data
);
377 DEBUG ("Reduce Accuracy changed");
379 self
->priv
->reduce_accuracy
= g_settings_get_boolean (gsettings_loc
, key
);
383 account_manager_prepared_cb (GObject
*source_object
,
384 GAsyncResult
*result
,
388 TpAccountManager
*account_manager
= TP_ACCOUNT_MANAGER (source_object
);
389 EmpathyLocationManager
*self
= user_data
;
390 GError
*error
= NULL
;
392 if (!tp_proxy_prepare_finish (account_manager
, result
, &error
))
394 DEBUG ("Failed to prepare account manager: %s", error
->message
);
395 g_error_free (error
);
399 accounts
= tp_account_manager_dup_valid_accounts (account_manager
);
400 for (l
= accounts
; l
!= NULL
; l
= l
->next
)
402 TpAccount
*account
= TP_ACCOUNT (l
->data
);
404 tp_g_signal_connect_object (account
, "status-changed",
405 G_CALLBACK (new_connection_cb
), self
, 0);
407 g_list_free_full (accounts
, g_object_unref
);
411 empathy_location_manager_init (EmpathyLocationManager
*self
)
413 EmpathyLocationManagerPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
414 EMPATHY_TYPE_LOCATION_MANAGER
, EmpathyLocationManagerPrivate
);
417 priv
->location
= tp_asv_new (NULL
, NULL
);
418 priv
->gsettings_loc
= g_settings_new (EMPATHY_PREFS_LOCATION_SCHEMA
);
420 /* Setup account status callbacks */
421 priv
->account_manager
= tp_account_manager_dup ();
423 tp_proxy_prepare_async (priv
->account_manager
, NULL
,
424 account_manager_prepared_cb
, self
);
426 /* Setup settings status callbacks */
427 g_signal_connect (priv
->gsettings_loc
,
428 "changed::" EMPATHY_PREFS_LOCATION_PUBLISH
,
429 G_CALLBACK (publish_cb
), self
);
431 g_signal_connect (priv
->gsettings_loc
,
432 "changed::" EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY
,
433 G_CALLBACK (reduce_accuracy_cb
), self
);
435 publish_cb (priv
->gsettings_loc
, EMPATHY_PREFS_LOCATION_PUBLISH
, self
);
436 reduce_accuracy_cb (priv
->gsettings_loc
, EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY
, self
);
439 EmpathyLocationManager
*
440 empathy_location_manager_dup_singleton (void)
442 return EMPATHY_LOCATION_MANAGER (g_object_new (EMPATHY_TYPE_LOCATION_MANAGER
,