2 * Copyright (C) 2008 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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Authors: Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
26 #include <glib/gi18n.h>
28 #include <telepathy-glib/util.h>
31 #include <geoclue/geoclue-master.h>
34 #include <extensions/extensions.h>
36 #include "empathy-location-manager.h"
37 #include "empathy-conf.h"
39 #include "libempathy/empathy-enum-types.h"
40 #include "libempathy/empathy-location.h"
41 #include "libempathy/empathy-contact-factory.h"
42 #include "libempathy/empathy-utils.h"
44 #define DEBUG_FLAG EMPATHY_DEBUG_LOCATION
45 #include "libempathy/empathy-debug.h"
47 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyLocationManager)
54 GeoclueResourceFlags resources;
55 GeoclueMasterClient *gc_client;
56 GeocluePosition *gc_position;
57 GeoclueAddress *gc_address;
59 } EmpathyLocationManagerPriv;
61 static void location_manager_finalize (GObject *object);
62 static void location_manager_get_property (GObject *object, guint param_id,
63 GValue *value, GParamSpec *pspec);
64 static void location_manager_set_property (GObject *object, guint param_id,
65 const GValue *value, GParamSpec *pspec);
67 static void position_changed_cb (GeocluePosition *position,
68 GeocluePositionFields fields, int timestamp, double latitude,
69 double longitude, double altitude, GeoclueAccuracy *accuracy,
71 static void address_changed_cb (GeoclueAddress *address, int timestamp,
72 GHashTable *details, GeoclueAccuracy *accuracy, gpointer user_data);
73 static void setup_geoclue (EmpathyLocationManager *location_manager);
74 static void publish_cb (EmpathyConf *conf, const gchar *key,
76 static void update_resources (EmpathyLocationManager *location_manager);
77 static void resource_cb (EmpathyConf *conf, const gchar *key,
81 G_DEFINE_TYPE (EmpathyLocationManager, empathy_location_manager, G_TYPE_OBJECT);
89 empathy_location_manager_class_init (EmpathyLocationManagerClass *class)
91 GObjectClass *object_class;
93 object_class = G_OBJECT_CLASS (class);
95 object_class->finalize = location_manager_finalize;
96 object_class->get_property = location_manager_get_property;
97 object_class->set_property = location_manager_set_property;
99 g_type_class_add_private (object_class, sizeof (EmpathyLocationManagerPriv));
103 publish_location (EmpathyLocationManager *location_manager, McAccount *account)
105 EmpathyLocationManagerPriv *priv;
106 guint connection_status = -1;
107 gboolean can_publish;
108 EmpathyConf *conf = empathy_conf_get ();
109 EmpathyContactFactory *factory = empathy_contact_factory_new ();
110 priv = GET_PRIV (location_manager);
112 if (!empathy_conf_get_bool (conf, EMPATHY_PREFS_LOCATION_PUBLISH, &can_publish))
118 connection_status = mission_control_get_connection_status (priv->mc,
121 if (connection_status != TP_CONNECTION_STATUS_CONNECTED)
124 DEBUG ("Publishing location to account %s", mc_account_get_display_name (account));
126 empathy_contact_factory_set_location (factory, account, priv->location);
130 publish_location_to_all_accounts (EmpathyLocationManager *location_manager)
132 GList *accounts = NULL, *l;
134 accounts = mc_accounts_list_by_enabled (TRUE);
135 for (l = accounts; l; l = l->next)
137 publish_location (location_manager, l->data);
140 mc_accounts_list_free (accounts);
144 account_status_changed_cb (MissionControl *mc,
145 TpConnectionStatus status,
147 TpConnectionStatusReason reason,
148 const gchar *unique_name,
149 gpointer *location_manager)
151 DEBUG ("Account %s changed status to %d", unique_name, status);
152 McAccount *account = mc_account_lookup (unique_name);
153 if (account && status == TP_CONNECTION_STATUS_CONNECTED)
154 publish_location (EMPATHY_LOCATION_MANAGER (location_manager), account);
158 empathy_location_manager_init (EmpathyLocationManager *location_manager)
161 EmpathyLocationManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (location_manager,
162 EMPATHY_TYPE_LOCATION_MANAGER, EmpathyLocationManagerPriv);
164 location_manager->priv = priv;
165 priv->is_setup = FALSE;
166 priv->mc = empathy_mission_control_new ();
167 priv->location = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
168 (GDestroyNotify) tp_g_value_slice_free);
170 // Setup settings status callbacks
171 conf = empathy_conf_get ();
172 empathy_conf_notify_add (conf, EMPATHY_PREFS_LOCATION_PUBLISH, publish_cb,
174 empathy_conf_notify_add (conf, EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK,
175 resource_cb, location_manager);
176 empathy_conf_notify_add (conf, EMPATHY_PREFS_LOCATION_RESOURCE_CELL,
177 resource_cb, location_manager);
178 empathy_conf_notify_add (conf, EMPATHY_PREFS_LOCATION_RESOURCE_GPS,
179 resource_cb, location_manager);
181 publish_cb (conf, EMPATHY_PREFS_LOCATION_PUBLISH, location_manager);
182 resource_cb (conf, EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK, location_manager);
183 resource_cb (conf, EMPATHY_PREFS_LOCATION_RESOURCE_CELL, location_manager);
184 resource_cb (conf, EMPATHY_PREFS_LOCATION_RESOURCE_GPS, location_manager);
186 // Setup account status callbacks
187 priv->token = empathy_connect_to_account_status_changed (priv->mc,
188 G_CALLBACK (account_status_changed_cb), location_manager, NULL);
193 location_manager_finalize (GObject *object)
195 EmpathyLocationManagerPriv *priv;
197 priv = GET_PRIV (object);
199 DEBUG ("finalize: %p", object);
201 G_OBJECT_CLASS (empathy_location_manager_parent_class)->finalize (object);
206 location_manager_get_property (GObject *object,
211 EmpathyLocationManagerPriv *priv;
213 priv = GET_PRIV (object);
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
225 location_manager_set_property (GObject *object,
230 EmpathyLocationManagerPriv *priv;
232 priv = GET_PRIV (object);
237 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
243 EmpathyLocationManager *
244 empathy_location_manager_get_default (void)
246 static EmpathyLocationManager *singleton = NULL;
247 if (singleton == NULL)
248 singleton = g_object_new (EMPATHY_TYPE_LOCATION_MANAGER, NULL);
255 position_changed_cb (GeocluePosition *position,
256 GeocluePositionFields fields,
261 GeoclueAccuracy *accuracy,
262 gpointer location_manager)
264 EmpathyLocationManagerPriv *priv;
265 priv = GET_PRIV (location_manager);
266 GeoclueAccuracyLevel level;
269 geoclue_accuracy_get_details (accuracy, &level, NULL, NULL);
270 DEBUG ("New position (accuracy level %d)", level);
271 if (level == GEOCLUE_ACCURACY_LEVEL_NONE)
274 if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
276 new_value = tp_g_value_slice_new (G_TYPE_DOUBLE);
277 g_value_set_double (new_value, latitude);
278 g_hash_table_insert (priv->location, EMPATHY_LOCATION_LON, new_value);
279 DEBUG ("\t - Longitude: %f", longitude);
281 else if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
283 new_value = tp_g_value_slice_new (G_TYPE_DOUBLE);
284 g_value_set_double (new_value, latitude);
285 g_hash_table_insert (priv->location, EMPATHY_LOCATION_LAT, new_value);
286 DEBUG ("\t - Latitude: %f", latitude);
288 else if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
290 new_value = tp_g_value_slice_new (G_TYPE_DOUBLE);
291 g_value_set_double (new_value, altitude);
292 g_hash_table_insert (priv->location, EMPATHY_LOCATION_ALT, new_value);
293 DEBUG ("\t - Altitude: %f", altitude);
296 if (level == GEOCLUE_ACCURACY_LEVEL_DETAILED)
298 gdouble mean, horizontal, vertical;
300 geoclue_accuracy_get_details (accuracy, &level, &horizontal, &vertical);
301 mean = (horizontal + vertical) / 2.0;
303 new_value = tp_g_value_slice_new (G_TYPE_DOUBLE);
304 g_value_set_double (new_value, mean);
305 g_hash_table_insert (priv->location, EMPATHY_LOCATION_ACCURACY, new_value);
306 DEBUG ("\t - Accuracy: %f", mean);
309 publish_location_to_all_accounts (EMPATHY_LOCATION_MANAGER (location_manager));
314 address_foreach_cb (gpointer key,
316 gpointer location_manager)
318 if (location_manager == NULL)
321 EmpathyLocationManagerPriv *priv;
322 priv = GET_PRIV (location_manager);
324 GValue *new_value = tp_g_value_slice_new (G_TYPE_STRING);
325 g_value_set_string (new_value, value);
327 g_hash_table_insert (priv->location, g_strdup (key), new_value);
328 DEBUG ("\t - %s: %s", (char*) key, (char*) value);
333 address_changed_cb (GeoclueAddress *address,
336 GeoclueAccuracy *accuracy,
337 gpointer location_manager)
339 GeoclueAccuracyLevel level;
340 geoclue_accuracy_get_details (accuracy, &level, NULL, NULL);
341 EmpathyLocationManagerPriv *priv;
343 DEBUG ("New address (accuracy level %d):", level);
345 priv = GET_PRIV (location_manager);
346 g_hash_table_remove_all (priv->location);
348 g_hash_table_foreach (details, address_foreach_cb, (gpointer)location_manager);
350 publish_location_to_all_accounts (EMPATHY_LOCATION_MANAGER (location_manager));
355 update_resources (EmpathyLocationManager *location_manager)
357 EmpathyLocationManagerPriv *priv;
359 priv = GET_PRIV (location_manager);
364 DEBUG ("Updating resources");
366 if (!geoclue_master_client_set_requirements (priv->gc_client,
367 GEOCLUE_ACCURACY_LEVEL_COUNTRY, 0, TRUE, priv->resources,
369 g_printerr ("set_requirements failed");
374 setup_geoclue (EmpathyLocationManager *location_manager)
376 EmpathyLocationManagerPriv *priv;
378 priv = GET_PRIV (location_manager);
380 GeoclueMaster *master;
381 GError *error = NULL;
383 DEBUG ("Setting up Geoclue");
384 master = geoclue_master_get_default ();
385 priv->gc_client = geoclue_master_create_client (master, NULL, NULL);
386 g_object_unref (master);
388 update_resources (location_manager);
390 /* Get updated when the position is changes */
391 priv->gc_position = geoclue_master_client_create_position (
392 priv->gc_client, &error);
393 if (priv->gc_position == NULL)
395 g_printerr ("Failed to create GeocluePosition: %s", error->message);
399 g_signal_connect (G_OBJECT (priv->gc_position), "position-changed",
400 G_CALLBACK (position_changed_cb), location_manager);
402 /* Get updated when the address changes */
403 priv->gc_address = geoclue_master_client_create_address (
404 priv->gc_client, &error);
405 if (priv->gc_address == NULL)
407 g_printerr ("Failed to create GeoclueAddress: %s", error->message);
411 g_signal_connect (G_OBJECT (priv->gc_address), "address-changed",
412 G_CALLBACK (address_changed_cb), location_manager);
414 priv->is_setup = TRUE;
418 publish_cb (EmpathyConf *conf,
422 EmpathyLocationManager *manager = EMPATHY_LOCATION_MANAGER (user_data);
423 EmpathyLocationManagerPriv *priv;
424 gboolean can_publish;
426 DEBUG ("Publish Conf changed");
427 priv = GET_PRIV (manager);
428 if (!empathy_conf_get_bool (conf, key, &can_publish))
434 setup_geoclue (manager);
435 publish_location_to_all_accounts (manager);
441 resource_cb (EmpathyConf *conf,
445 EmpathyLocationManager *manager = EMPATHY_LOCATION_MANAGER (user_data);
446 EmpathyLocationManagerPriv *priv;
447 GeoclueResourceFlags resource = 0;
448 gboolean resource_enabled;
450 priv = GET_PRIV (manager);
451 DEBUG ("A Resource Conf changed");
453 if (empathy_conf_get_bool (conf, key, &resource_enabled))
455 if (strcmp (key, EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK))
456 resource = GEOCLUE_RESOURCE_NETWORK;
457 if (strcmp (key, EMPATHY_PREFS_LOCATION_RESOURCE_CELL))
458 resource = GEOCLUE_RESOURCE_CELL;
459 if (strcmp (key, EMPATHY_PREFS_LOCATION_RESOURCE_GPS))
460 resource = GEOCLUE_RESOURCE_GPS;
462 if (resource_enabled)
463 priv->resources |= resource;
465 priv->resources &= resource;
467 update_resources (manager);