2 * position-publisher.c - Source for PositionPublisher
3 * Copyright (C) 2010 Guillaume Desmottes
4 * @author Guillaume Desmottes <gdesmott@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/interfaces.h>
28 #include <location/location-gps-device.h>
30 #include "connection-watcher.h"
31 #include "position-publisher.h"
33 G_DEFINE_TYPE(PositionPublisher, position_publisher, G_TYPE_OBJECT)
44 SIG_HAS_CONNECTIONS_CHANGED,
48 static guint signals[LAST_SIGNAL] = { 0 };
50 /* Minimum time before 2 publishing (in seconds) */
51 #define PUBLISH_THROTTLE 10
53 /* private structure */
54 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
56 struct _PositionPublisherPrivate
58 ConnectionWatcher *watcher;
59 LocationGPSDevice *gps_device;
60 /* List of (TpConnection *) supporting location publishing */
63 /* If not 0, we are waiting before publishing again */
64 guint throttle_timeout;
65 /* TRUE if location has been modified while we were waiting */
69 gboolean dispose_has_run;
72 #define POSITION_PUBLISHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
74 static void conn_invalidated_cb (TpProxy *conn,
78 PositionPublisher *self);
80 static void publish_to_all (PositionPublisher *self);
83 remove_connection (PositionPublisher *self,
86 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
88 g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
90 priv->connections = g_slist_remove (priv->connections, conn);
91 g_object_unref (conn);
93 if (g_slist_length (priv->connections) == 0)
95 g_print ("We don't have location connection any more\n");
96 g_signal_emit (self, signals[SIG_HAS_CONNECTIONS_CHANGED], 0, FALSE);
101 set_location_cb (TpConnection *conn,
104 GObject *weak_object)
106 PositionPublisher *self = POSITION_PUBLISHER (weak_object);
110 g_print ("SetLocation failed (%s): %s\n", tp_proxy_get_object_path (conn),
113 if (error->code == TP_ERROR_NOT_IMPLEMENTED)
115 g_print ("remove connection\n");
116 remove_connection (self, conn);
122 g_print ("SetLocation succeed (%s)\n", tp_proxy_get_object_path (conn));
126 publish_throttle_timeout_cb (gpointer data)
128 PositionPublisher *self = data;
129 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
131 priv->throttle_timeout = 0;
135 publish_to_all (self);
136 priv->modified = FALSE;
143 publish_to_conn (PositionPublisher *self,
146 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
148 if (priv->location == NULL)
151 tp_cli_connection_interface_location_call_set_location (conn, -1,
152 priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
156 publish_to_all (PositionPublisher *self)
158 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
161 if (priv->throttle_timeout != 0)
165 for (l = priv->connections; l != NULL; l = g_slist_next (l))
167 TpConnection *conn = l->data;
169 publish_to_conn (self, conn);
172 /* We won't publish during the next PUBLISH_THROTTLE seconds */
173 priv->throttle_timeout = g_timeout_add_seconds (PUBLISH_THROTTLE,
174 publish_throttle_timeout_cb, self);
178 update_position (PositionPublisher *self,
184 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
188 /* Truncate at 1 decimal place */
189 lon = ((int) (lon * 10)) / 10.0;
190 lat = ((int) (lat * 10)) / 10.0;
192 /* FIXME: change accuracy (not that easy as accuracy is in meters) */
195 g_print ("update position: lat: %f lon: %f alt: %f accuracy: %f\n",
196 lat, lon, alt, accuracy);
198 if (priv->location != NULL)
199 g_hash_table_unref (priv->location);
201 priv->location = tp_asv_new (
202 "timestamp", G_TYPE_INT64, (gint64) time (NULL),
203 "lat", G_TYPE_DOUBLE, lat,
204 "lon", G_TYPE_DOUBLE, lon,
205 "alt", G_TYPE_DOUBLE, alt,
206 "accuracy", G_TYPE_DOUBLE, accuracy,
209 priv->modified = TRUE;
211 publish_to_all (self);
215 location_changed_cb (LocationGPSDevice *device,
216 PositionPublisher *self)
221 if (device->fix == NULL)
224 if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
227 update_position (self, device->fix->latitude, device->fix->longitude,
228 device->fix->altitude, device->fix->eph / 100.0);
232 conn_invalidated_cb (TpProxy *conn,
236 PositionPublisher *self)
238 g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
241 remove_connection (self, TP_CONNECTION (conn));
245 connection_added_cb (ConnectionWatcher *watcher,
247 PositionPublisher *self)
249 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
251 if (g_slist_find (priv->connections, conn) != NULL)
254 if (!tp_proxy_has_interface_by_id (conn,
255 TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
258 priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
260 if (g_slist_length (priv->connections) == 1)
262 /* We just added the first connection */
263 g_print ("We have location connection\n");
264 g_signal_emit (self, signals[SIG_HAS_CONNECTIONS_CHANGED], 0, TRUE);
267 g_signal_connect (conn, "invalidated",
268 G_CALLBACK (conn_invalidated_cb), self);
270 publish_to_conn (self, conn);
274 position_publisher_init (PositionPublisher *obj)
276 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
278 priv->watcher = connection_watcher_new ();
280 g_signal_connect (priv->watcher, "connection-added",
281 G_CALLBACK (connection_added_cb), obj);
283 priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
285 g_signal_connect (priv->gps_device, "changed",
286 G_CALLBACK (location_changed_cb), obj);
288 priv->connections = NULL;
292 position_publisher_constructed (GObject *object)
294 PositionPublisher *self = POSITION_PUBLISHER (object);
295 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
297 connection_watcher_start (priv->watcher);
299 if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
300 G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
303 static void position_publisher_dispose (GObject *object);
304 static void position_publisher_finalize (GObject *object);
305 static void position_publisher_get_property (GObject *object,
306 guint property_id, GValue *value, GParamSpec *pspec);
307 static void position_publisher_set_property (GObject *object,
308 guint property_id, const GValue *value, GParamSpec *pspec);
311 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
313 GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
314 GParamSpec *param_spec;
316 g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
318 object_class->dispose = position_publisher_dispose;
319 object_class->finalize = position_publisher_finalize;
320 object_class->get_property = position_publisher_get_property;
321 object_class->set_property = position_publisher_set_property;
323 object_class->constructed = position_publisher_constructed;
325 param_spec = g_param_spec_boolean ("blur", "Blur?",
326 "Whether the real GPS position is truncated for privacy concerns.", TRUE,
327 G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE
328 | G_PARAM_STATIC_STRINGS);
329 g_object_class_install_property (object_class, PROP_BLUR, param_spec);
331 signals[SIG_HAS_CONNECTIONS_CHANGED] = g_signal_new (
332 "has-connections-changed",
333 G_OBJECT_CLASS_TYPE (object_class),
337 g_cclosure_marshal_VOID__BOOLEAN,
343 position_publisher_dispose (GObject *object)
345 PositionPublisher *self = POSITION_PUBLISHER (object);
346 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
349 if (priv->dispose_has_run)
352 priv->dispose_has_run = TRUE;
354 g_object_unref (priv->watcher);
355 g_object_unref (priv->gps_device);
357 for (l = priv->connections; l != NULL; l = g_slist_next (l))
359 g_object_unref (l->data);
362 g_hash_table_unref (priv->location);
364 if (priv->throttle_timeout != 0)
365 g_source_remove (priv->throttle_timeout);
367 if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
368 G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
372 position_publisher_finalize (GObject *object)
374 PositionPublisher *self = POSITION_PUBLISHER (object);
375 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
377 g_slist_free (priv->connections);
379 G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
383 position_publisher_get_property (GObject *object,
388 PositionPublisher *self = POSITION_PUBLISHER (object);
389 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
391 switch (property_id) {
393 g_value_set_boolean (value, priv->blur);
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
402 position_publisher_set_property (GObject *object,
407 PositionPublisher *self = POSITION_PUBLISHER (object);
408 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
410 switch (property_id) {
412 priv->blur = g_value_get_boolean (value);
417 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423 position_publisher_new (void)
425 return g_object_new (POSITION_PUBLISHER_TYPE,
430 position_publisher_set_blur (PositionPublisher *self,
433 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
435 g_print ("%s blurring location\n", blur ? "Start": "Stop");
437 g_object_notify (G_OBJECT (self), "blur");
441 position_publisher_has_connections (PositionPublisher *self)
443 PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
445 return g_slist_length (priv->connections) > 0;